jdskjda
This commit is contained in:
parent
5a5fa05667
commit
679cc8d7f4
188
client/package-lock.json
generated
188
client/package-lock.json
generated
@ -17,7 +17,7 @@
|
||||
"jquery": "^3.7.1",
|
||||
"jquery-ui": "^1.13.3",
|
||||
"jquery-ui-dist": "^1.13.3",
|
||||
"marked": "^9.1.0",
|
||||
"marked": "^9.1.6",
|
||||
"marked-katex-extension": "^4.0.1",
|
||||
"mitt": "^3.0.1",
|
||||
"prismjs": "^1.29.0",
|
||||
@ -25,8 +25,7 @@
|
||||
"three": "^0.164.1",
|
||||
"vue": "^3.3.4",
|
||||
"vue-draggable": "^2.0.6",
|
||||
"vue-router": "^4.2.4",
|
||||
"vue-simplemde": "^2.1.1"
|
||||
"vue-router": "^4.2.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "^4.3.4",
|
||||
@ -271,82 +270,6 @@
|
||||
"integrity": "sha512-GcIY79elgB+azP74j8vqkiXz8xLFfIzbQJdlwOPisgbKT00tviJQuEghOXSMVxJ00HoYJbGswr4kcllUc4xCcg==",
|
||||
"deprecated": "Potential XSS vulnerability patched in v6.0.0."
|
||||
},
|
||||
"node_modules/@codemirror/autocomplete": {
|
||||
"version": "6.9.2",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.9.2.tgz",
|
||||
"integrity": "sha512-suItGf7PhtfgQMCd8ofYzycdsAHDBB8BkNrmyxeLvptW7yNT6zGT6ZzwhAfmB94TUyAAStrHjaDGC4/foenF2A==",
|
||||
"dependencies": {
|
||||
"@codemirror/language": "^6.0.0",
|
||||
"@codemirror/state": "^6.0.0",
|
||||
"@codemirror/view": "^6.17.0",
|
||||
"@lezer/common": "^1.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@codemirror/language": "^6.0.0",
|
||||
"@codemirror/state": "^6.0.0",
|
||||
"@codemirror/view": "^6.0.0",
|
||||
"@lezer/common": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@codemirror/commands": {
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.3.0.tgz",
|
||||
"integrity": "sha512-tFfcxRIlOWiQDFhjBSWJ10MxcvbCIsRr6V64SgrcaY0MwNk32cUOcCuNlWo8VjV4qRQCgNgUAnIeo0svkk4R5Q==",
|
||||
"dependencies": {
|
||||
"@codemirror/language": "^6.0.0",
|
||||
"@codemirror/state": "^6.2.0",
|
||||
"@codemirror/view": "^6.0.0",
|
||||
"@lezer/common": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@codemirror/language": {
|
||||
"version": "6.9.1",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.9.1.tgz",
|
||||
"integrity": "sha512-lWRP3Y9IUdOms6DXuBpoWwjkR7yRmnS0hKYCbSfPz9v6Em1A1UCRujAkDiCrdYfs1Z0Eu4dGtwovNPStIfkgNA==",
|
||||
"dependencies": {
|
||||
"@codemirror/state": "^6.0.0",
|
||||
"@codemirror/view": "^6.0.0",
|
||||
"@lezer/common": "^1.1.0",
|
||||
"@lezer/highlight": "^1.0.0",
|
||||
"@lezer/lr": "^1.0.0",
|
||||
"style-mod": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@codemirror/lint": {
|
||||
"version": "6.4.2",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.4.2.tgz",
|
||||
"integrity": "sha512-wzRkluWb1ptPKdzlsrbwwjYCPLgzU6N88YBAmlZi8WFyuiEduSd05MnJYNogzyc8rPK7pj6m95ptUApc8sHKVA==",
|
||||
"dependencies": {
|
||||
"@codemirror/state": "^6.0.0",
|
||||
"@codemirror/view": "^6.0.0",
|
||||
"crelt": "^1.0.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@codemirror/search": {
|
||||
"version": "6.5.4",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.5.4.tgz",
|
||||
"integrity": "sha512-YoTrvjv9e8EbPs58opjZKyJ3ewFrVSUzQ/4WXlULQLSDDr1nGPJ67mMXFNNVYwdFhybzhrzrtqgHmtpJwIF+8g==",
|
||||
"dependencies": {
|
||||
"@codemirror/state": "^6.0.0",
|
||||
"@codemirror/view": "^6.0.0",
|
||||
"crelt": "^1.0.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@codemirror/state": {
|
||||
"version": "6.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.2.1.tgz",
|
||||
"integrity": "sha512-RupHSZ8+OjNT38zU9fKH2sv+Dnlr8Eb8sl4NOnnqz95mCFTZUaiRP8Xv5MeeaG0px2b8Bnfe7YGwCV3nsBhbuw=="
|
||||
},
|
||||
"node_modules/@codemirror/view": {
|
||||
"version": "6.21.3",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.21.3.tgz",
|
||||
"integrity": "sha512-8l1aSQ6MygzL4Nx7GVYhucSXvW4jQd0F6Zm3v9Dg+6nZEfwzJVqi4C2zHfDljID+73gsQrWp9TgHc81xU15O4A==",
|
||||
"dependencies": {
|
||||
"@codemirror/state": "^6.1.4",
|
||||
"style-mod": "^4.1.0",
|
||||
"w3c-keyname": "^2.2.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@discoveryjs/json-ext": {
|
||||
"version": "0.5.7",
|
||||
"resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz",
|
||||
@ -915,27 +838,6 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@lezer/common": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.1.0.tgz",
|
||||
"integrity": "sha512-XPIN3cYDXsoJI/oDWoR2tD++juVrhgIago9xyKhZ7IhGlzdDM9QgC8D8saKNCz5pindGcznFr2HBSsEQSWnSjw=="
|
||||
},
|
||||
"node_modules/@lezer/highlight": {
|
||||
"version": "1.1.6",
|
||||
"resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.1.6.tgz",
|
||||
"integrity": "sha512-cmSJYa2us+r3SePpRCjN5ymCqCPv+zyXmDl0ciWtVaNiORT/MxM7ZgOMQZADD0o51qOaOg24qc/zBViOIwAjJg==",
|
||||
"dependencies": {
|
||||
"@lezer/common": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@lezer/lr": {
|
||||
"version": "1.3.13",
|
||||
"resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.3.13.tgz",
|
||||
"integrity": "sha512-RLAbau/4uSzKgIKj96mI5WUtG1qtiR0Frn0Ei9zhPj8YOkHM+1Bb8SgdVvmR/aWJCFIzjo2KFnDiRZ75Xf5NdQ==",
|
||||
"dependencies": {
|
||||
"@lezer/common": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@mrmlnc/readdir-enhanced": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz",
|
||||
@ -3407,28 +3309,6 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/codemirror": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/codemirror/-/codemirror-6.0.1.tgz",
|
||||
"integrity": "sha512-J8j+nZ+CdWmIeFIGXEFbFPtpiYacFMDR8GlHK3IyHQJMCaVRfGx9NT+Hxivv1ckLWPvNdZqndbr/7lVhrf/Svg==",
|
||||
"dependencies": {
|
||||
"@codemirror/autocomplete": "^6.0.0",
|
||||
"@codemirror/commands": "^6.0.0",
|
||||
"@codemirror/language": "^6.0.0",
|
||||
"@codemirror/lint": "^6.0.0",
|
||||
"@codemirror/search": "^6.0.0",
|
||||
"@codemirror/state": "^6.0.0",
|
||||
"@codemirror/view": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/codemirror-spell-checker": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/codemirror-spell-checker/-/codemirror-spell-checker-1.1.2.tgz",
|
||||
"integrity": "sha512-2Tl6n0v+GJRsC9K3MLCdLaMOmvWL0uukajNJseorZJsslaxZyZMgENocPU8R0DyoTAiKsyqiemSOZo7kjGV0LQ==",
|
||||
"dependencies": {
|
||||
"typo-js": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/collection-visit": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz",
|
||||
@ -3854,11 +3734,6 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/crelt": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz",
|
||||
"integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g=="
|
||||
},
|
||||
"node_modules/cross-spawn": {
|
||||
"version": "7.0.3",
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
|
||||
@ -8055,9 +7930,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/marked": {
|
||||
"version": "9.1.0",
|
||||
"resolved": "https://registry.npmjs.org/marked/-/marked-9.1.0.tgz",
|
||||
"integrity": "sha512-VZjm0PM5DMv7WodqOUps3g6Q7dmxs9YGiFUZ7a2majzQTTCgX+6S6NAJHPvOhgFBzYz8s4QZKWWMfZKFmsfOgA==",
|
||||
"version": "9.1.6",
|
||||
"resolved": "https://registry.npmjs.org/marked/-/marked-9.1.6.tgz",
|
||||
"integrity": "sha512-jcByLnIFkd5gSXZmjNvS1TlmRhCXZjIzHYlaGkPlLIekG55JDR2Z4va9tZwCiP+/RDERiNhMOFu01xd6O5ct1Q==",
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"marked": "bin/marked.js"
|
||||
},
|
||||
@ -10860,16 +10736,6 @@
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/simplemde": {
|
||||
"version": "1.11.2",
|
||||
"resolved": "https://registry.npmjs.org/simplemde/-/simplemde-1.11.2.tgz",
|
||||
"integrity": "sha512-AUXuHJ/tEEDEcN/MTitHIw3AuBcheizJt7SVwtyn00B0UK5RKZ3GB+JndmRcJ5wfYGCIL0O2yJm/uz0sJOFSLg==",
|
||||
"dependencies": {
|
||||
"codemirror": "*",
|
||||
"codemirror-spell-checker": "*",
|
||||
"marked": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/sirv": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/sirv/-/sirv-2.0.4.tgz",
|
||||
@ -11449,11 +11315,6 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/style-mod": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.0.tgz",
|
||||
"integrity": "sha512-Ca5ib8HrFn+f+0n4N4ScTIA9iTOQ7MaGS1ylHcoVqW9J7w2w8PzN6g9gKmTYgGEBH8e120+RCmhpje6jC5uGWA=="
|
||||
},
|
||||
"node_modules/stylehacks": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.1.tgz",
|
||||
@ -11828,11 +11689,6 @@
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/typo-js": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/typo-js/-/typo-js-1.2.3.tgz",
|
||||
"integrity": "sha512-67Hyl94beZX8gmTap7IDPrG5hy2cHftgsCAcGvE1tzuxGT+kRB+zSBin0wIMwysYw8RUCBCvv9UfQl8TNM75dA=="
|
||||
},
|
||||
"node_modules/uc.micro": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz",
|
||||
@ -12207,26 +12063,6 @@
|
||||
"vue": "^3.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/vue-simplemde": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/vue-simplemde/-/vue-simplemde-2.1.1.tgz",
|
||||
"integrity": "sha512-UBjjdcTVlq7MrmeeKyzGVFEdsqarsqVxL/QGuCCfV1jGs7ve2wJ3pTENM6FEvaSURFnDJhIR6uUkPrxHg6S7HQ==",
|
||||
"dependencies": {
|
||||
"marked": "^3.0.8",
|
||||
"simplemde": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/vue-simplemde/node_modules/marked": {
|
||||
"version": "3.0.8",
|
||||
"resolved": "https://registry.npmjs.org/marked/-/marked-3.0.8.tgz",
|
||||
"integrity": "sha512-0gVrAjo5m0VZSJb4rpL59K1unJAMb/hm8HRXqasD8VeC8m91ytDPMritgFSlKonfdt+rRYYpP/JfLxgIX8yoSw==",
|
||||
"bin": {
|
||||
"marked": "bin/marked"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 12"
|
||||
}
|
||||
},
|
||||
"node_modules/vue-style-loader": {
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/vue-style-loader/-/vue-style-loader-4.1.3.tgz",
|
||||
@ -12245,11 +12081,6 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/w3c-keyname": {
|
||||
"version": "2.2.8",
|
||||
"resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz",
|
||||
"integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ=="
|
||||
},
|
||||
"node_modules/watchpack": {
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.1.tgz",
|
||||
@ -12803,9 +12634,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/xss": {
|
||||
"version": "1.0.14",
|
||||
"resolved": "https://registry.npmjs.org/xss/-/xss-1.0.14.tgz",
|
||||
"integrity": "sha512-og7TEJhXvn1a7kzZGQ7ETjdQVS2UfZyTlsEdDOqvQF7GoxNfY+0YLCzBy1kPdsDDx4QuNAonQPddpsn6Xl/7sw==",
|
||||
"version": "1.0.15",
|
||||
"resolved": "https://registry.npmjs.org/xss/-/xss-1.0.15.tgz",
|
||||
"integrity": "sha512-FVdlVVC67WOIPvfOwhoMETV72f6GbW7aOabBC3WxN/oUdoEMDyLz4OgRv5/gck2ZeNqEQu+Tb0kloovXOfpYVg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"commander": "^2.20.3",
|
||||
"cssfilter": "0.0.10"
|
||||
|
@ -18,7 +18,7 @@
|
||||
"jquery": "^3.7.1",
|
||||
"jquery-ui": "^1.13.3",
|
||||
"jquery-ui-dist": "^1.13.3",
|
||||
"marked": "^9.1.0",
|
||||
"marked": "^9.1.6",
|
||||
"marked-katex-extension": "^4.0.1",
|
||||
"mitt": "^3.0.1",
|
||||
"prismjs": "^1.29.0",
|
||||
@ -26,8 +26,7 @@
|
||||
"three": "^0.164.1",
|
||||
"vue": "^3.3.4",
|
||||
"vue-draggable": "^2.0.6",
|
||||
"vue-router": "^4.2.4",
|
||||
"vue-simplemde": "^2.1.1"
|
||||
"vue-router": "^4.2.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "^4.3.4",
|
||||
|
@ -118,11 +118,23 @@ input[type=text], input[type=password], input[type=email] {
|
||||
border: solid 1px var(--color-border);
|
||||
}
|
||||
|
||||
|
||||
textarea {
|
||||
background-color: var(--color-background-softer);
|
||||
padding: 12px;
|
||||
color: var(--color-text);
|
||||
border: none;
|
||||
}
|
||||
|
||||
input[type=text]:focus, input[type=password]:focus, input[type=email]:focus {
|
||||
outline: none;
|
||||
background-color: var(--color-background-softest);
|
||||
}
|
||||
|
||||
textarea:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
button {
|
||||
margin-top: 5px;
|
||||
margin-bottom: 5px;
|
||||
@ -178,11 +190,16 @@ button:active {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.centered {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.window-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border: solid 1px var(--color-border);
|
||||
|
||||
/* opacity: 0; */
|
||||
|
||||
user-select: none;
|
||||
-webkit-box-shadow: 0px 0px 10px -2px rgba(0,0,0,0.75);
|
||||
@ -199,6 +216,10 @@ button:active {
|
||||
font-size: 32px;
|
||||
}
|
||||
|
||||
.document b {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.text-icon {
|
||||
height: 18px;
|
||||
width: 18px;
|
||||
|
@ -1,35 +1,14 @@
|
||||
import './assets/main.css'
|
||||
import './assets/prism.css'
|
||||
|
||||
import { createApp, reactive } from 'vue'
|
||||
import { createApp, defineComponent, reactive } from 'vue'
|
||||
import App from './App.vue'
|
||||
import router from './router'
|
||||
|
||||
import mitt from 'mitt';
|
||||
const emitter = mitt();
|
||||
|
||||
import VueMarkdownEditor from '@kangc/v-md-editor';
|
||||
import '@kangc/v-md-editor/lib/style/base-editor.css';
|
||||
import vuepressTheme from '@kangc/v-md-editor/lib/theme/vuepress.js';
|
||||
import '@kangc/v-md-editor/lib/theme/style/vuepress.css';
|
||||
import esEs from '@kangc/v-md-editor/lib/lang/es-ES'
|
||||
|
||||
|
||||
import Prism from 'prismjs';
|
||||
|
||||
import 'prismjs/components/prism-python';
|
||||
import 'prismjs/components/prism-javascript';
|
||||
import 'prismjs/components/prism-java';
|
||||
import 'prismjs/components/prism-c';
|
||||
import 'prismjs/components/prism-cpp';
|
||||
import 'prismjs/components/prism-csharp';
|
||||
import 'prismjs/components/prism-ruby';
|
||||
import 'prismjs/components/prism-bash';
|
||||
|
||||
VueMarkdownEditor.lang.use('es-Es', esEs);
|
||||
VueMarkdownEditor.use(vuepressTheme, { Prism });
|
||||
|
||||
const app = createApp(App).use(VueMarkdownEditor);
|
||||
const app = createApp(App);
|
||||
app.config.globalProperties.emitter = emitter
|
||||
app.config.globalProperties.rollWindows = {
|
||||
login: reactive([]),
|
||||
|
52
client/src/services/Campaign.js
Normal file
52
client/src/services/Campaign.js
Normal file
@ -0,0 +1,52 @@
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { ExitGame } from "./Game";
|
||||
import { socket } from "./Socket";
|
||||
import { GetUser } from "./User";
|
||||
import { chat } from './Chat';
|
||||
import { ClearAll, CreateWindow } from './Windows';
|
||||
|
||||
|
||||
let _currentCampaign = null;
|
||||
let _currentPlayer = null;
|
||||
const _players = ref([]);
|
||||
|
||||
function ConnectToCampaign(campaign){
|
||||
_currentCampaign = campaign;
|
||||
chat.value = [];
|
||||
|
||||
socket.emit('enter', GetUser(), _currentCampaign._id);
|
||||
}
|
||||
|
||||
function Disconnect(){
|
||||
socket.emit('exit');
|
||||
ExitGame();
|
||||
|
||||
_currentCampaign = null;
|
||||
_currentPlayer = null;
|
||||
chat.value = [];
|
||||
}
|
||||
|
||||
|
||||
function DisplayCampaign(data = currentCampaign){
|
||||
ClearAll();
|
||||
CreateWindow('campaign_preview', {campaign: data});
|
||||
}
|
||||
|
||||
socket.on('update-players', data => {
|
||||
_players.value = [];
|
||||
Object.keys(data).forEach((key) => {
|
||||
_players.value.push(data[key]);
|
||||
if(GetUser()._id == data[key].user._id) _currentPlayer = data[key];
|
||||
});
|
||||
})
|
||||
|
||||
export {
|
||||
_currentCampaign,
|
||||
_currentPlayer,
|
||||
_players,
|
||||
|
||||
ConnectToCampaign,
|
||||
DisplayCampaign,
|
||||
Disconnect
|
||||
}
|
36
client/src/services/Chat.js
Normal file
36
client/src/services/Chat.js
Normal file
@ -0,0 +1,36 @@
|
||||
import { ref } from 'vue';
|
||||
import { socket } from "./Socket";
|
||||
|
||||
let chatMessageId = 0;
|
||||
const chat = ref([]);
|
||||
|
||||
socket.on('message', (data) => {
|
||||
// Add new chat message, ?
|
||||
if(chat.value.length > 0) if(chat.value[chat.value.length - 1].author == data.author){
|
||||
chat.value[chat.value.length - 1].chunkSize += 1;
|
||||
chat.value[chat.value.length - 1].chunks.push({
|
||||
id: chat.value[chat.value.length - 1].chunkSize,
|
||||
type: data.type ? data.type : 'text',
|
||||
content: data.content
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
chatMessageId += 1;
|
||||
chat.value.push({
|
||||
id: chatMessageId,
|
||||
author: data.author,
|
||||
chunkSize: 1,
|
||||
chunks: [
|
||||
{
|
||||
id: 1,
|
||||
type: data.type ? data.type : 'text',
|
||||
content: data.content
|
||||
}
|
||||
]
|
||||
});
|
||||
});
|
||||
|
||||
export {
|
||||
chat
|
||||
}
|
@ -32,6 +32,12 @@ function PopulateContext(val){
|
||||
spanInfo.innerHTML = element.name;
|
||||
contextMenuElement.appendChild(spanInfo);
|
||||
|
||||
if(element.icon){
|
||||
let iconContextElement = document.createElement('img');
|
||||
iconContextElement.src = element.icon;
|
||||
contextMenuElement.appendChild(iconContextElement);
|
||||
}
|
||||
|
||||
if(element.context){
|
||||
let iconContextElement = document.createElement('img');
|
||||
iconContextElement.src = arrowIcon;
|
||||
|
41
client/src/services/Data.js
Normal file
41
client/src/services/Data.js
Normal file
@ -0,0 +1,41 @@
|
||||
import Api from '@/services/Api'
|
||||
import { GetCampaign } from "./Dragonroll";
|
||||
import { socket } from './Socket';
|
||||
import { reactive } from 'vue';
|
||||
|
||||
let data = reactive({});
|
||||
|
||||
function InitData(){
|
||||
data.value = {
|
||||
concepts: [],
|
||||
};
|
||||
}
|
||||
|
||||
function FetchConcepts(){
|
||||
Api().get('/concept/list?campaign=' + GetCampaign()._id).then(response => {
|
||||
data.value.concepts = response.data.data;
|
||||
}).catch((err) => console.log(err));
|
||||
}
|
||||
|
||||
function FetchData(){
|
||||
FetchConcepts();
|
||||
}
|
||||
|
||||
|
||||
socket.on('update-concepts', () => {
|
||||
FetchConcepts();
|
||||
});
|
||||
|
||||
let GetConcepts = () => data.value.concepts;
|
||||
let GetConcept = (id) => Api().get('/concept/get?campaign=' + GetCampaign()._id + "&id=" + id)
|
||||
|
||||
|
||||
export {
|
||||
InitData,
|
||||
FetchData,
|
||||
|
||||
FetchConcepts,
|
||||
|
||||
GetConcepts,
|
||||
GetConcept,
|
||||
}
|
@ -1,12 +1,7 @@
|
||||
import { ref } from 'vue';
|
||||
import { ClearAll, ClearWindow, CreateWindow } from './Windows';
|
||||
import { io } from "socket.io-client";
|
||||
import Api from '@/services/Api'
|
||||
import { backendUrl } from './BackendURL';
|
||||
import { GetUser } from './User';
|
||||
import { ExitGame } from './Game';
|
||||
import { GetModule } from './Modules';
|
||||
import { GetMap, LoadMap, UpdateMapList } from './Map';
|
||||
import { socket } from './Socket';
|
||||
import { _currentCampaign, _currentPlayer, _players } from './Campaign';
|
||||
import { chat } from './Chat';
|
||||
|
||||
let emitter;
|
||||
|
||||
@ -20,62 +15,12 @@ function DisplayToast(color, text, duration = 1000){
|
||||
emitter.emit("toast", {color, text, duration});
|
||||
}
|
||||
|
||||
const socket = io(backendUrl)
|
||||
let GetPlayerList = () => _players;
|
||||
let GetCampaign = () => _currentCampaign;
|
||||
let GetClient = () => _currentPlayer;
|
||||
|
||||
let currentCampaign = null;
|
||||
let currentPlayer = null;
|
||||
|
||||
const players = ref([]);
|
||||
let GetPlayerList = () => { return players; };
|
||||
let GetCampaign = () => { return currentCampaign; };
|
||||
let GetClient = () => { return currentPlayer; };
|
||||
|
||||
let chatMessageId = 0;
|
||||
const chat = ref([]);
|
||||
let GetChatRef = () => chat;
|
||||
|
||||
socket.on('change_map', data => {
|
||||
console.log("ChangeMap")
|
||||
UpdateMapList().then(() => {
|
||||
LoadMap(GetMap(data.id));
|
||||
});
|
||||
})
|
||||
|
||||
socket.on('update-players', data => {
|
||||
players.value = [];
|
||||
Object.keys(data).forEach((key) => {
|
||||
players.value.push(data[key]);
|
||||
if(GetUser()._id == data[key].user._id) currentPlayer = data[key];
|
||||
});
|
||||
})
|
||||
|
||||
socket.on('message', (data) => {
|
||||
// Add new chat message, ?
|
||||
if(chat.value.length > 0) if(chat.value[chat.value.length - 1].author == data.author){
|
||||
chat.value[chat.value.length - 1].chunkSize += 1;
|
||||
chat.value[chat.value.length - 1].chunks.push({
|
||||
id: chat.value[chat.value.length - 1].chunkSize,
|
||||
type: data.type ? data.type : 'text',
|
||||
content: data.content
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
chatMessageId += 1;
|
||||
chat.value.push({
|
||||
id: chatMessageId,
|
||||
author: data.author,
|
||||
chunkSize: 1,
|
||||
chunks: [
|
||||
{
|
||||
id: 1,
|
||||
type: data.type ? data.type : 'text',
|
||||
content: data.content
|
||||
}
|
||||
]
|
||||
});
|
||||
});
|
||||
|
||||
function _SendMap(id){
|
||||
socket.emit('send_map', {id});
|
||||
}
|
||||
@ -84,30 +29,10 @@ function SendMessage(data){
|
||||
socket.emit('message', data);
|
||||
}
|
||||
|
||||
function DisplayCampaign(data = currentCampaign){
|
||||
ClearAll();
|
||||
CreateWindow('campaign_preview', {campaign: data});
|
||||
}
|
||||
|
||||
function ConnectToCampaign(campaign){
|
||||
currentCampaign = campaign;
|
||||
chat.value = [];
|
||||
|
||||
socket.emit('enter', GetUser(), currentCampaign._id);
|
||||
}
|
||||
|
||||
function Disconnect(){
|
||||
socket.emit('exit');
|
||||
ExitGame();
|
||||
|
||||
currentCampaign = null;
|
||||
currentPlayer = null;
|
||||
chat.value = [];
|
||||
}
|
||||
|
||||
function GetPlayer(player_campaign){
|
||||
let index = players.value.findIndex((p) => {return p._id == player_campaign});
|
||||
if(index != -1) return players.value[index];
|
||||
let index = _players.value.findIndex((p) => {return p._id == player_campaign});
|
||||
if(index != -1) return _players.value[index];
|
||||
}
|
||||
|
||||
function GetSystem(){
|
||||
@ -115,16 +40,11 @@ function GetSystem(){
|
||||
}
|
||||
|
||||
export {
|
||||
socket,
|
||||
SetEmitter,
|
||||
GetEmitter,
|
||||
|
||||
DisplayToast,
|
||||
|
||||
DisplayCampaign,
|
||||
ConnectToCampaign,
|
||||
Disconnect,
|
||||
|
||||
GetCampaign,
|
||||
GetClient,
|
||||
GetPlayerList,
|
||||
|
@ -1,10 +1,13 @@
|
||||
import { ref } from "vue";
|
||||
import { FetchData, InitData } from "./Data";
|
||||
|
||||
const inGameRef = ref(false);
|
||||
let InGameRef = () => inGameRef;
|
||||
|
||||
function LaunchGame(){
|
||||
inGameRef.value = true;
|
||||
InitData();
|
||||
FetchData();
|
||||
}
|
||||
|
||||
function ExitGame(){
|
||||
|
@ -3,6 +3,7 @@ import { initCustomFormatter, ref, toRaw } from 'vue';
|
||||
import Api from '@/services/Api'
|
||||
import { _SendMap, GetCampaign } from './Dragonroll';
|
||||
import { backendUrl } from './BackendURL';
|
||||
import { socket } from './Socket';
|
||||
|
||||
function dataURLtoFile(dataurl, filename) {
|
||||
var arr = dataurl.split(","),
|
||||
@ -292,6 +293,13 @@ function ChangeBackgroundColor(color){
|
||||
SaveMap(currentMapId.value);
|
||||
}
|
||||
|
||||
|
||||
socket.on('change_map', data => {
|
||||
UpdateMapList().then(() => {
|
||||
LoadMap(GetMap(data.id));
|
||||
});
|
||||
})
|
||||
|
||||
export {
|
||||
toMapX,
|
||||
toMapY,
|
||||
|
8
client/src/services/Socket.js
Normal file
8
client/src/services/Socket.js
Normal file
@ -0,0 +1,8 @@
|
||||
import { io } from "socket.io-client";
|
||||
|
||||
import { backendUrl } from './BackendURL';
|
||||
const socket = io(backendUrl);
|
||||
|
||||
export {
|
||||
socket
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
import { reactive, ref } from 'vue'
|
||||
import { Disconnect } from './Dragonroll';
|
||||
import { Disconnect } from './Campaign';
|
||||
|
||||
const windows = ref([])
|
||||
|
||||
|
@ -4,7 +4,7 @@ import { InGameRef } from '../../services/Game';
|
||||
import IconButton from '../partials/game/IconButton.vue';
|
||||
import { AddSound } from '../../services/Sound';
|
||||
import TileMap from './TileMap.vue';
|
||||
import { DisplayCampaign, GetCampaign, GetClient } from '../../services/Dragonroll';
|
||||
import { GetCampaign, GetClient } from '../../services/Dragonroll';
|
||||
import { ClearAll, ClearWindow, CreateWindow } from '../../services/Windows';
|
||||
|
||||
const game = ref(null);
|
||||
|
@ -1,5 +1,5 @@
|
||||
<script setup>
|
||||
import { ref, reactive, defineComponent } from 'vue'
|
||||
import { ref, reactive, defineComponent, TransitionGroup } from 'vue'
|
||||
import { RouterLink, RouterView } from 'vue-router'
|
||||
import { defineAsyncComponent } from 'vue'
|
||||
|
||||
@ -79,12 +79,23 @@ InjectSystemWindows('dnd-5e')
|
||||
|
||||
<template>
|
||||
<div class="window-container" :key="reload">
|
||||
<component v-for="win in windows" :is="WindowMap[win.type]" :key="win.id" :data="win"></component>
|
||||
<TransitionGroup name="window">
|
||||
<component v-for="win in windows" :is="WindowMap[win.type]" :key="win.id" :data="win"></component>
|
||||
</TransitionGroup>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
<style>
|
||||
.window-enter-active,
|
||||
.window-leave-active {
|
||||
transition: all 0.15s ease;
|
||||
}
|
||||
.window-enter-from,
|
||||
.window-leave-to {
|
||||
opacity: 0;
|
||||
transform: translateY(15px);
|
||||
}
|
||||
|
||||
.window-wrapper {
|
||||
background-color: var(--window-background);
|
||||
@ -95,7 +106,6 @@ InjectSystemWindows('dnd-5e')
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
@ -3,9 +3,8 @@
|
||||
import { onMounted, ref } from 'vue';
|
||||
|
||||
import Api from '@/services/Api'
|
||||
import { DisplayCampaign } from '@/services/Dragonroll'
|
||||
import { AddSound } from '../../services/Sound';
|
||||
import { ConnectToCampaign } from '../../services/Dragonroll';
|
||||
import { ConnectToCampaign, DisplayCampaign } from '../../services/Campaign';
|
||||
|
||||
const props = defineProps(['data']);
|
||||
const data = props.data;
|
||||
|
@ -41,7 +41,6 @@ watch(chat, () => {
|
||||
if(chat.value.length > 0) if(chat.value[chat.value.length - 1].author == GetClient()._id){
|
||||
setTimeout(() => {
|
||||
messageContainer.value.scrollTop = messageContainer.value.scrollHeight;
|
||||
console.log(messageContainer.value.scrollHeight)
|
||||
}, 0);
|
||||
}
|
||||
}, {deep: true})
|
||||
|
@ -27,11 +27,12 @@ function OpenConcept(element){
|
||||
|
||||
<template>
|
||||
<div class="list-container" ref="listContainer">
|
||||
|
||||
<div class="list-element" v-for="element in elements" :key="element._id" v-on:click.prevent="OpenConcept(element)">
|
||||
<img :src="element.info ? element.info.icon : 'icons/game-icons/ffffff/lorc/crossed-swords.svg'" class="concept-icon">
|
||||
<span class="title">{{ element.name }}</span>
|
||||
</div>
|
||||
<TransitionGroup name="list-element">
|
||||
<div class="list-element" v-for="element in elements" :key="element._id" v-on:click.prevent="OpenConcept(element)">
|
||||
<img :src="element.info ? element.info.icon : 'icons/game-icons/ffffff/lorc/crossed-swords.svg'" class="concept-icon">
|
||||
<span class="title">{{ element.name }}</span>
|
||||
</div>
|
||||
</TransitionGroup>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -57,10 +58,29 @@ function OpenConcept(element){
|
||||
}
|
||||
}
|
||||
|
||||
.list-element-move, /* apply transition to moving elements */
|
||||
.list-element-enter-active,
|
||||
.list-element-leave-active {
|
||||
transition: all 0.5s ease;
|
||||
}
|
||||
|
||||
.list-element-enter-from,
|
||||
.list-element-leave-to {
|
||||
opacity: 0;
|
||||
transform: translateX(30px);
|
||||
}
|
||||
|
||||
/* ensure leaving items are taken out of layout flow so that moving
|
||||
animations can be calculated correctly. */
|
||||
.list-element-leave-active {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.list-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
</style>
|
@ -1,6 +1,6 @@
|
||||
<script setup>
|
||||
|
||||
import { onMounted, ref, getCurrentInstance, defineExpose } from 'vue';
|
||||
import { onMounted, ref, getCurrentInstance } from 'vue';
|
||||
import { ClearWindow, CreateChildWindow } from '../../services/Windows';
|
||||
|
||||
const image = ref(null);
|
||||
|
94
client/src/views/partials/MarkdownEditor.vue
Normal file
94
client/src/views/partials/MarkdownEditor.vue
Normal file
@ -0,0 +1,94 @@
|
||||
<script setup>
|
||||
import { onMounted, ref, watch } from 'vue';
|
||||
import { marked } from "https://cdn.jsdelivr.net/npm/marked/lib/marked.esm.js";
|
||||
|
||||
const props = defineProps(['done']);
|
||||
|
||||
import IconButton from '../partials/game/IconButton.vue';
|
||||
|
||||
const editing = ref(false);
|
||||
const editor = ref(null);
|
||||
const preview = ref(null);
|
||||
const text = ref("");
|
||||
|
||||
function EditContent(){
|
||||
editing.value = true;
|
||||
}
|
||||
|
||||
function PreviewContent(){
|
||||
editing.value = false;
|
||||
|
||||
preview.value.innerHTML = marked.parse(editor.value.value);
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
watch(text, () => {
|
||||
editor.value.value = text.value;
|
||||
preview.value.innerHTML = marked.parse(editor.value.value);
|
||||
});
|
||||
|
||||
editor.value.addEventListener("change", () => {
|
||||
props.done(editor.value.value);
|
||||
});
|
||||
|
||||
editor.value.value = props.text ?? '';
|
||||
preview.value.innerHTML = marked.parse(editor.value.value);
|
||||
})
|
||||
|
||||
defineExpose({
|
||||
text,
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="markdown-editor">
|
||||
<div class="editor-content">
|
||||
<div v-show="!editing" class="preview">
|
||||
<div class="document preview" ref="preview">
|
||||
</div>
|
||||
|
||||
<div class="fixed-bottom-buttons">
|
||||
<IconButton icon="icons/iconoir/regular/edit-pencil.svg" :action="EditContent"></IconButton>
|
||||
</div>
|
||||
</div>
|
||||
<div v-show="editing" class="editor">
|
||||
<textarea class="editing" ref="editor"></textarea>
|
||||
|
||||
<div class="fixed-bottom-buttons">
|
||||
<IconButton icon="icons/iconoir/solid/eye.svg" :action="PreviewContent"></IconButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.editing, .editor, .editor-content {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.editing {
|
||||
background-color: var(--color-background);
|
||||
}
|
||||
|
||||
.markdown-editor {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: var(--color-background);
|
||||
}
|
||||
.fixed-bottom-buttons {
|
||||
position: absolute;
|
||||
bottom: 10px;
|
||||
right: 10px;
|
||||
z-index: 2;
|
||||
display: flex;
|
||||
}
|
||||
.preview {
|
||||
padding: 8px 0 8px 8px;
|
||||
overflow-y: auto;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
101
client/src/views/partials/Tabs.vue
Normal file
101
client/src/views/partials/Tabs.vue
Normal file
@ -0,0 +1,101 @@
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
const props = defineProps(['rows']);
|
||||
const rows = ref(props.rows);
|
||||
|
||||
const rowDict = {}
|
||||
for(let i = 0; i < props.rows.length; i++) rowDict[props.rows[i].replace(/\s+/g, '-').toLowerCase()] = i;
|
||||
let selectedTab = ref(props.rows[0].replace(/\s+/g, '-').toLowerCase());
|
||||
|
||||
function SelectTab(row){
|
||||
selectedTab.value = row;
|
||||
console.log(selectedTab.value);
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<template>
|
||||
<div class="tab-container">
|
||||
<div class="row">
|
||||
<div class="toggler" :class="{ selected: row.replace(/\s+/g, '-').toLowerCase() == selectedTab }" v-for="row in rows" v-on:click.prevent="SelectTab(row.replace(/\s+/g, '-').toLowerCase())">
|
||||
{{ row }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="tab-container-outer">
|
||||
<div v-for="row in rows" class="tab-content">
|
||||
<TransitionGroup name="tab">
|
||||
<div class="tab-content-inner" v-show="row.replace(/\s+/g, '-').toLowerCase() == selectedTab" :key="row">
|
||||
<slot :name="row.replace(/\s+/g, '-').toLowerCase()" />
|
||||
</div>
|
||||
</TransitionGroup>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
.toggler.selected {
|
||||
color: var(--color-text);
|
||||
background-color: var(--color-background-softer);
|
||||
}
|
||||
</style>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.tab-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.tab-container-outer {
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.tab-enter-active,
|
||||
.tab-leave-active {
|
||||
transition: all 0.15s ease;
|
||||
}
|
||||
.tab-enter-from,
|
||||
.tab-leave-to {
|
||||
opacity: 0;
|
||||
transform: translateY(15px);
|
||||
}
|
||||
|
||||
.tab-content {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.tab-content-inner {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.toggler {
|
||||
flex-grow: 1;
|
||||
flex-basis: 0;
|
||||
font-weight: bold;
|
||||
padding: 3px 3px 3px 12px;
|
||||
font-size: 16px;
|
||||
|
||||
color: #9c9c9c;
|
||||
border-left: 1px solid var(--color-border);
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
border-top: 1px solid var(--color-border);
|
||||
transition: color 0.2s, background-color 0.2s;
|
||||
|
||||
&:first-child {
|
||||
border-left: none;
|
||||
}
|
||||
|
||||
&.active {
|
||||
color: var(--color-text);
|
||||
}
|
||||
}
|
||||
</style>
|
87
client/src/views/partials/Tags.vue
Normal file
87
client/src/views/partials/Tags.vue
Normal file
@ -0,0 +1,87 @@
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import IconButton from './game/IconButton.vue';
|
||||
import { HideContextMenu, ShowContextMenu } from '../../services/ContextMenu';
|
||||
const props = defineProps(['items', 'done']);
|
||||
const items = ref(props.items);
|
||||
const done = props.done;
|
||||
|
||||
const itemDict = {}
|
||||
|
||||
function lower(text) {
|
||||
return text.replace(/\s+/g, '-').toLowerCase();
|
||||
}
|
||||
|
||||
for(let i = 0; i < props.items.length; i++) itemDict[lower(items.value[i])] = i;
|
||||
let selectedTags = ref([]);
|
||||
|
||||
function SelectTab(tag){
|
||||
if(!selectedTags.value.includes(tag)) selectedTags.value.push(tag);
|
||||
done(selectedTags.value);
|
||||
}
|
||||
|
||||
function RemoveTag(tag){
|
||||
selectedTags.value = selectedTags.value.filter((item) => item !== tag);
|
||||
done(selectedTags.value);
|
||||
}
|
||||
|
||||
function OpenDropdown(){
|
||||
let context = [];
|
||||
items.value.forEach(name => {
|
||||
context.push({
|
||||
icon: selectedTags.value.includes(name) ? 'icons/iconoir/regular/check.svg' : false,
|
||||
name,
|
||||
action: () => {
|
||||
HideContextMenu();
|
||||
if(!selectedTags.value.includes(name)){
|
||||
SelectTab(name);
|
||||
} else { RemoveTag(name) }
|
||||
}
|
||||
});
|
||||
});
|
||||
ShowContextMenu(context);
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="tags-container">
|
||||
<div class="tag" v-for="tag in selectedTags" :key="tag">
|
||||
<span>{{ tag }}</span>
|
||||
<img class="close-tag" src="icons/iconoir/regular/xmark.svg" v-on:click.prevent="RemoveTag(tag)">
|
||||
</div>
|
||||
<IconButton class="small-icon" icon="icons/iconoir/regular/plus.svg" :action="OpenDropdown"></IconButton>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.tags-container {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
padding-left: 4px;
|
||||
padding-right: 4px;
|
||||
}
|
||||
|
||||
.small-icon {
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
float: left;
|
||||
transform: translateY(2px);
|
||||
}
|
||||
.tag {
|
||||
width: auto;
|
||||
float: left;
|
||||
line-height: 20px;
|
||||
padding: 2px;
|
||||
border-radius: 4px;
|
||||
margin: 4px 2px 0 2px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background-color: #303030;
|
||||
|
||||
.close-tag {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
filter: invert(0.9);
|
||||
}
|
||||
}
|
||||
</style>
|
@ -59,7 +59,7 @@ function RefreshCampaigns(){
|
||||
<!-- Body -->
|
||||
<div class="campaign-list-container">
|
||||
<div class="window-second-header">
|
||||
<h2>Your campaigns</h2>
|
||||
<h2 class="centered">Your campaigns</h2>
|
||||
|
||||
<div class="campaign-list">
|
||||
<CampaignEntry v-for="camp in myCampaigns" :key="camp._id" :data="camp"></CampaignEntry>
|
||||
@ -67,7 +67,7 @@ function RefreshCampaigns(){
|
||||
</div>
|
||||
|
||||
<div class="window-second-header">
|
||||
<h2>Other campaigns</h2>
|
||||
<h2 class="centered">Other campaigns</h2>
|
||||
<div class="campaign-list">
|
||||
<CampaignEntry v-for="camp in otherCampaigns" :key="camp._id" :data="camp"></CampaignEntry>
|
||||
</div>
|
||||
|
@ -4,7 +4,7 @@ import { SetupHandle, SetSize, SetPosition, ResetPosition } from '@/services/Win
|
||||
|
||||
import WindowHandle from '@/views/partials/WindowHandle.vue';
|
||||
import PlayerList from '../../partials/PlayerList.vue';
|
||||
import { Disconnect, DisplayToast, GetCampaign, GetClient } from '../../../services/Dragonroll';
|
||||
import { DisplayToast, GetClient } from '../../../services/Dragonroll';
|
||||
import CampaignBookList from '../../partials/books/CampaignBookList.vue';
|
||||
import { ClearAll, ClearWindow, CreateWindow, SetMinSize, SetResizable } from '../../../services/Windows';
|
||||
import { LaunchGame } from '../../../services/Game';
|
||||
@ -13,6 +13,7 @@ import ChatComponent from '../../partials/ChatComponent.vue';
|
||||
import GameSystem from '@/views/partials/GameSystem.vue'
|
||||
import { GetModule } from '../../../services/Modules';
|
||||
import { AddTooltip } from '../../../services/Tooltip';
|
||||
import { Disconnect } from '../../../services/Campaign';
|
||||
|
||||
const handle = ref(null);
|
||||
|
||||
@ -75,7 +76,7 @@ function Exit(){
|
||||
|
||||
<div class="campaign-preview-container" :class="hide_chat ? 'campaign-preview-compact' : ''" ref="container">
|
||||
<div class="campaign-preview-column left">
|
||||
<h2>Players</h2>
|
||||
<h2 class="centered">Players</h2>
|
||||
<PlayerList :campaign="data.campaign"></PlayerList>
|
||||
<div class="buttons-row">
|
||||
<button class="btn-primary button-row sound-click" v-on:click.prevent="CopyCode" ref="copy_code_button">Copy invite code</button>
|
||||
|
@ -1,12 +1,12 @@
|
||||
<script setup>
|
||||
import WindowHandle from '@/views/partials/WindowHandle.vue';
|
||||
import Api from '@/services/Api'
|
||||
|
||||
import { onMounted, ref, shallowRef } from 'vue';
|
||||
import { onMounted, ref, shallowRef, watch } from 'vue';
|
||||
import { ClearWindow, CreateWindow, ResetPosition, SetMinSize, SetResizable, SetSize, SetupHandle } from '../../../services/Windows';
|
||||
import IconButton from '@/views/partials/game/IconButton.vue'
|
||||
import ConceptList from '../../partials/ConceptList.vue';
|
||||
import { GetCampaign, socket } from '../../../services/Dragonroll';
|
||||
import { FetchConcepts, GetConcepts } from '../../../services/Data';
|
||||
import Tabs from '../../partials/Tabs.vue';
|
||||
|
||||
const handle = ref(null);
|
||||
|
||||
@ -26,23 +26,14 @@ onMounted(() => {
|
||||
SetResizable(id, true);
|
||||
SetMinSize(id, {width: 350, height: 300});
|
||||
|
||||
FetchConcepts();
|
||||
|
||||
socket.on('update-concepts', () => {
|
||||
console.log("!!!");
|
||||
FetchConcepts();
|
||||
});
|
||||
});
|
||||
|
||||
function FetchConcepts(){
|
||||
Api().get('/concept/list?campaign=' + GetCampaign()._id).then(response => {
|
||||
// console.log(response.data);
|
||||
elements.value = response.data.data;
|
||||
console.log(elements);
|
||||
watch(GetConcepts, () => {
|
||||
console.log("Updated???")
|
||||
}).catch((err) => console.log(err));
|
||||
}
|
||||
|
||||
elements.value = GetConcepts();
|
||||
console.log(elements);
|
||||
});
|
||||
|
||||
FetchConcepts();
|
||||
});
|
||||
function OpenCreateItemPrompt(){
|
||||
CreateWindow('create_item_prompt', {id: 'create_item_prompt', title: 'Create Item', close: () => ClearWindow('create_item_prompt')})
|
||||
}
|
||||
@ -54,12 +45,11 @@ function OpenCreateItemPrompt(){
|
||||
<WindowHandle :window="id" ref="handle"></WindowHandle>
|
||||
|
||||
<div class="main-container">
|
||||
<div class="row">
|
||||
<div class="toggler">Items</div>
|
||||
<div class="toggler">Spells</div>
|
||||
<div class="toggler">Features</div>
|
||||
</div>
|
||||
<ConceptList :elements="elements"></ConceptList>
|
||||
<Tabs :rows="['Items', 'Spells', 'Features']">
|
||||
<template #items>
|
||||
<ConceptList :elements="elements"></ConceptList>
|
||||
</template>
|
||||
</Tabs>
|
||||
</div>
|
||||
|
||||
|
||||
@ -75,27 +65,6 @@ function OpenCreateItemPrompt(){
|
||||
height: calc(100% - 24px);
|
||||
}
|
||||
|
||||
.toggler {
|
||||
flex-grow: 1;
|
||||
font-weight: bold;
|
||||
padding: 10px;
|
||||
font-size: 16px;
|
||||
|
||||
color: #9c9c9c;
|
||||
border-left: 1px solid var(--color-border);
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
border-top: 1px solid var(--color-border);
|
||||
transition: color 1s;
|
||||
|
||||
&:first-child {
|
||||
border-left: none;
|
||||
}
|
||||
|
||||
&.active {
|
||||
color: var(--color-text);
|
||||
}
|
||||
}
|
||||
|
||||
.fixed-bottom-buttons {
|
||||
position: absolute;
|
||||
bottom: 10px;
|
||||
|
@ -4,10 +4,14 @@ import Api from '@/services/Api'
|
||||
|
||||
import { onMounted, ref, shallowRef } from 'vue';
|
||||
import { SetupHandle, SetSize, ResetPosition } from '@/services/Windows';
|
||||
import { CreateWindow } from '../../../../services/Windows';
|
||||
import { CreateWindow, SetMinSize, SetResizable } from '../../../../services/Windows';
|
||||
import IconSelector from '../../../partials/IconSelector.vue';
|
||||
import { AddContextMenu, HideContextMenu, ShowContextMenu } from '../../../../services/ContextMenu';
|
||||
import { GetCampaign } from '../../../../services/Dragonroll';
|
||||
import { GetConcept } from '../../../../services/Data';
|
||||
import Tabs from '../../../partials/Tabs.vue';
|
||||
import MarkdownEditor from '../../../partials/MarkdownEditor.vue';
|
||||
import Tags from '../../../partials/Tags.vue';
|
||||
const props = defineProps(['data']);
|
||||
const data = props.data;
|
||||
|
||||
@ -17,6 +21,7 @@ const rarity = ref(null);
|
||||
const weaponType = ref(null);
|
||||
const item_name = ref(null);
|
||||
const icon_selector = ref(null);
|
||||
const description = ref(null);
|
||||
|
||||
function GenRarities(){
|
||||
let rarities = [];
|
||||
@ -76,6 +81,17 @@ function IconSelected(val){
|
||||
Upload();
|
||||
}
|
||||
|
||||
function DescriptionChanged(text){
|
||||
SetParam('description', text);
|
||||
Upload();
|
||||
}
|
||||
|
||||
function PropertiesChanged(properties){
|
||||
console.log(properties);
|
||||
SetParam('properties', properties);
|
||||
Upload();
|
||||
}
|
||||
|
||||
function InitValues(){
|
||||
let rarities = GenRarities();
|
||||
let weapon_types = GenTypes(["", "Melee", "Ranged", "Martial Melee", "Martial Ranged", "Natural", "Improvised", "Siege Weapon"]);
|
||||
@ -86,6 +102,7 @@ function InitValues(){
|
||||
if(concept.value.info.icon) icon_selector.value.icon = concept.value.info.icon;
|
||||
if(concept.value.info.rarity) rarity.value.innerHTML = `<span class='important ${concept.value.info.rarity.replace(/\s+/g, '-').toLowerCase()}'>${concept.value.info.rarity}</span>`;
|
||||
if(concept.value.info.weapon_type) weaponType.value.innerHTML = `<span class='important'>${concept.value.info.weapon_type}</span>`;
|
||||
if(concept.value.info.description) description.value.text = concept.value.info.description;
|
||||
|
||||
rarity.value.addEventListener("click", () => {
|
||||
ShowContextMenu(rarities)
|
||||
@ -106,7 +123,9 @@ function InitValues(){
|
||||
onMounted(() => {
|
||||
|
||||
SetupHandle(id, handle);
|
||||
SetSize(id, {width: 500, height: 400});
|
||||
SetSize(id, {width: 600, height: 700});
|
||||
SetResizable(id, true);
|
||||
SetMinSize(id, {width: 400, height: 300});
|
||||
ResetPosition(id, "center");
|
||||
item_type.value = data.item_type;
|
||||
|
||||
@ -123,7 +142,7 @@ onMounted(() => {
|
||||
}).catch(err => console.log(err));
|
||||
} else {
|
||||
// Get concept
|
||||
Api().get('/concept/get?campaign=' + GetCampaign()._id + "&id=" + data.item_id).then(response => {
|
||||
GetConcept(data.item_id).then(response => {
|
||||
concept.value = response.data.concept;
|
||||
InitValues();
|
||||
}).catch(err => console.log(err));
|
||||
@ -148,6 +167,23 @@ onMounted(() => {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Tabs :rows="['Description', 'Details']">
|
||||
<template #description>
|
||||
<div class="description-container">
|
||||
<div class="description-sidebar">
|
||||
<p>Hola</p>
|
||||
</div>
|
||||
<div class="description">
|
||||
<MarkdownEditor ref="description" :done="DescriptionChanged"></MarkdownEditor>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template #details>
|
||||
<h2 class="section">Properties</h2>
|
||||
<Tags :items="['Amunnition','Finesse','Heavy','Light','Loading','Range','Reach','Special','Thrown','Two-Handed','Versatile']" :done="PropertiesChanged"></Tags>
|
||||
<h2 class="section">Damage</h2>
|
||||
</template>
|
||||
</Tabs>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -165,6 +201,28 @@ onMounted(() => {
|
||||
border: 1px solid var(--color-border);
|
||||
}
|
||||
|
||||
h2.section {
|
||||
margin-left: 12px;
|
||||
font-family: NodestoCapsCondensed;
|
||||
font-size: 32px;
|
||||
line-height: 48px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.description-container {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
|
||||
.description-sidebar {
|
||||
min-width: 200px;
|
||||
}
|
||||
|
||||
.description {
|
||||
flex-grow: 1;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.item-header {
|
||||
display: flex;
|
||||
|
Loading…
Reference in New Issue
Block a user