More things work now
All checks were successful
Build and Deploy Nuxt / build (push) Successful in 34s
All checks were successful
Build and Deploy Nuxt / build (push) Successful in 34s
This commit is contained in:
@@ -6,6 +6,7 @@ import WindowManager from './components/managers/WindowManager.vue';
|
|||||||
import { CreateWindow } from '@/services/Windows'
|
import { CreateWindow } from '@/services/Windows'
|
||||||
import { GetUser, HasAdmin } from './services/User';
|
import { GetUser, HasAdmin } from './services/User';
|
||||||
import TooltipManager from './components/managers/TooltipManager.vue';
|
import TooltipManager from './components/managers/TooltipManager.vue';
|
||||||
|
import ContextMenuManager from './components/managers/ContextMenuManager.vue';
|
||||||
|
|
||||||
async function start(){
|
async function start(){
|
||||||
if(GetUser()){
|
if(GetUser()){
|
||||||
@@ -41,6 +42,7 @@ onMounted(() => {
|
|||||||
<div class="viewer">
|
<div class="viewer">
|
||||||
<ToastManager></ToastManager>
|
<ToastManager></ToastManager>
|
||||||
<TooltipManager></TooltipManager>
|
<TooltipManager></TooltipManager>
|
||||||
|
<ContextMenuManager></ContextMenuManager>
|
||||||
<WindowManager></WindowManager>
|
<WindowManager></WindowManager>
|
||||||
<ContentManager></ContentManager>
|
<ContentManager></ContentManager>
|
||||||
<!-- Managers -->
|
<!-- Managers -->
|
||||||
|
|||||||
61
frontend/app/components/layouts/Dropdown.vue
Normal file
61
frontend/app/components/layouts/Dropdown.vue
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
<script setup>
|
||||||
|
import { onMounted, ref, watch } from 'vue';
|
||||||
|
import { AddContextMenu, HideContextMenu } from '@/services/ContextMenu';
|
||||||
|
const props = defineProps(['options', 'onselect', 'selected']);
|
||||||
|
const options = props.options;
|
||||||
|
const selectCallback = props.onselect;
|
||||||
|
const initialSelect = props.selected;
|
||||||
|
const dropdown = ref(null);
|
||||||
|
|
||||||
|
const selected = ref(initialSelect);
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
let context = [];
|
||||||
|
if(props.selected == undefined) selected.value = "undefined";
|
||||||
|
watch(() => props.selected, () => {
|
||||||
|
selected.value = props.selected;
|
||||||
|
});
|
||||||
|
options.forEach(name => {
|
||||||
|
context.push({
|
||||||
|
icon: selected.value == name ? 'icons/iconoir/regular/check.svg' : false,
|
||||||
|
name,
|
||||||
|
action: () => {
|
||||||
|
HideContextMenu();
|
||||||
|
selected.value = name;
|
||||||
|
if(selectCallback) selectCallback(name);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
AddContextMenu(dropdown.value, context, {dropdown: true});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="dropdown" ref="dropdown">
|
||||||
|
<span>{{ selected }}</span>
|
||||||
|
<img class="icon" src="/icons/iconoir/regular/nav-arrow-down.svg" draggable="false" ref="closeButton">
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.dropdown {
|
||||||
|
flex-grow: 1;
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
background-color: var(--color-background-softer);
|
||||||
|
border: none;
|
||||||
|
padding: 4px 8px 4px 8px;
|
||||||
|
margin: 0 6px 0px 6px;
|
||||||
|
border-radius: 6px;
|
||||||
|
color: var(--color-text);
|
||||||
|
transition: 300ms background-color;
|
||||||
|
border: solid 1px var(--color-border);
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
margin-left: auto;
|
||||||
|
justify-content: right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
101
frontend/app/components/layouts/Tabs.vue
Normal file
101
frontend/app/components/layouts/Tabs.vue
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue';
|
||||||
|
const props = defineProps(['rows']);
|
||||||
|
|
||||||
|
const rowDict = {}
|
||||||
|
for(let i = 0; i < props.rows.length; i++) rowDict[props.rows[i].id] = i;
|
||||||
|
let selectedTab = ref(props.rows[0].id);
|
||||||
|
|
||||||
|
function SelectTab(row){
|
||||||
|
selectedTab.value = row;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="tab-container">
|
||||||
|
<div class="row">
|
||||||
|
<div class="toggler" :class="{ selected: row.id == selectedTab }" v-for="row in rows" v-on:click.prevent="SelectTab(row.id)">
|
||||||
|
{{ $t(row.value) }}
|
||||||
|
</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.id == selectedTab" :key="row.id">
|
||||||
|
<slot :name="row.id" />
|
||||||
|
</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%;
|
||||||
|
width: 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;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-content-inner {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
pointer-events: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggler {
|
||||||
|
flex-grow: 1;
|
||||||
|
flex-basis: 0;
|
||||||
|
font-weight: bold;
|
||||||
|
padding: 3px 12px 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>
|
||||||
72
frontend/app/components/managers/ContextMenuManager.vue
Normal file
72
frontend/app/components/managers/ContextMenuManager.vue
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
<script setup>
|
||||||
|
import { onMounted, watch, ref } from 'vue';
|
||||||
|
import { SetupContextMenu } from '../../services/ContextMenu';
|
||||||
|
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
SetupContextMenu();
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div id="context-menu" class="context-menu">
|
||||||
|
<!--
|
||||||
|
<div class="context-menu-element">
|
||||||
|
<span>Hola</span> <img src="/icons/iconoir/regular/nav-arrow-right.svg">
|
||||||
|
</div>
|
||||||
|
<div class="context-menu-element">
|
||||||
|
<span>Holaa</span>
|
||||||
|
</div>
|
||||||
|
<div class="context-menu-element">
|
||||||
|
<span>Holaa</span>
|
||||||
|
</div>
|
||||||
|
<div class="context-menu-element">
|
||||||
|
<span>Holaaaaaaa</span>
|
||||||
|
</div>
|
||||||
|
-->
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.context-menu {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 214748363;
|
||||||
|
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
|
||||||
|
.context-menu-element {
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
border-width: 1px 1px 1px 1px;
|
||||||
|
}
|
||||||
|
border: solid 1px var(--color-border);
|
||||||
|
border-width: 1px 1px 0px 1px;
|
||||||
|
padding: 3px 5px 3px 5px;
|
||||||
|
cursor: default;
|
||||||
|
user-select: none;
|
||||||
|
background-color: var(--tooltip-background);
|
||||||
|
transition: background-color 100ms;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
span {
|
||||||
|
flex-grow: 1;
|
||||||
|
padding-right: 20px;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
filter: invert(1);
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--color-background-softest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -55,12 +55,12 @@ function EditProfile(){
|
|||||||
}
|
}
|
||||||
|
|
||||||
function EditSettings(){
|
function EditSettings(){
|
||||||
ClearWindow('main_menu');
|
ClearWindow({type: 'main_menu'});
|
||||||
CreateWindow('settings', {
|
CreateWindow('settings', {
|
||||||
id: 'settings',
|
id: 'settings',
|
||||||
type: 'settings',
|
type: 'settings',
|
||||||
title: 'settings.title',
|
title: 'settings.title',
|
||||||
back: () => { ClearWindow('settings'); CreateWindow('main_menu'); }
|
back: () => { ClearWindow({type: 'settings'}); CreateWindow({type: 'main_menu'}); }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { onMounted, ref } from 'vue';
|
import { onMounted, ref } from 'vue';
|
||||||
import { SetupHandle, SetSize, ResetPosition } from '@/services/Windows';
|
import { SetupHandle, SetSize, ResetPosition, Top } from '@/services/Windows';
|
||||||
|
|
||||||
import Server from '@/services/Server'
|
import Server from '@/services/Server'
|
||||||
import { SetMinSize, SetResizable } from '@/services/Windows';
|
import { SetMinSize, SetResizable } from '@/services/Windows';
|
||||||
@@ -16,12 +16,14 @@ const data = props.data;
|
|||||||
const userIcon = ref("");
|
const userIcon = ref("");
|
||||||
|
|
||||||
const handle = ref(null);
|
const handle = ref(null);
|
||||||
|
const wrapper = ref(null);
|
||||||
const isAdmin = ref(false);
|
const isAdmin = ref(false);
|
||||||
|
|
||||||
let id = data.id;
|
let id = data.id;
|
||||||
console.log(data);
|
console.log(data);
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
Top(wrapper);
|
||||||
SetupHandle(id, handle);
|
SetupHandle(id, handle);
|
||||||
SetSize(id, {width: 500, height: 480});
|
SetSize(id, {width: 500, height: 480});
|
||||||
ResetPosition(id, "center");
|
ResetPosition(id, "center");
|
||||||
@@ -44,7 +46,7 @@ function RemoveUser(){
|
|||||||
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="window-wrapper" :id="'window-wrapper-' + id">
|
<div class="window-wrapper" :id="'window-wrapper-' + id" ref="wrapper">
|
||||||
<WindowHandle :window="id" ref="handle"></WindowHandle>
|
<WindowHandle :window="id" ref="handle"></WindowHandle>
|
||||||
|
|
||||||
<BigIconTemplate :title="data.user.username" :img="userIcon">
|
<BigIconTemplate :title="data.user.username" :img="userIcon">
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { onMounted, ref } from 'vue';
|
import { onMounted, ref } from 'vue';
|
||||||
import { SetupHandle, SetSize, ResetPosition } from '@/services/Windows';
|
import { SetupHandle, SetSize, ResetPosition, Top } from '@/services/Windows';
|
||||||
|
|
||||||
import WindowHandle from './partials/WindowHandle.vue';
|
import WindowHandle from './partials/WindowHandle.vue';
|
||||||
|
|
||||||
const handle = ref(null);
|
const handle = ref(null);
|
||||||
|
const wrapper = ref(null);
|
||||||
|
|
||||||
const props = defineProps(['data']);
|
const props = defineProps(['data']);
|
||||||
const data = props.data;
|
const data = props.data;
|
||||||
@@ -12,6 +13,7 @@ const data = props.data;
|
|||||||
let id = data.id;
|
let id = data.id;
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
Top(wrapper);
|
||||||
SetupHandle(id, handle);
|
SetupHandle(id, handle);
|
||||||
SetSize(id, {width: 500, height: 380});
|
SetSize(id, {width: 500, height: 380});
|
||||||
ResetPosition(id, "center");
|
ResetPosition(id, "center");
|
||||||
@@ -20,7 +22,7 @@ onMounted(() => {
|
|||||||
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="window-wrapper" :id="'window-wrapper-' + id">
|
<div class="window-wrapper" :id="'window-wrapper-' + id" ref="wrapper">
|
||||||
<WindowHandle :window="id" ref="handle"></WindowHandle>
|
<WindowHandle :window="id" ref="handle"></WindowHandle>
|
||||||
|
|
||||||
<!-- Body -->
|
<!-- Body -->
|
||||||
|
|||||||
@@ -5,9 +5,9 @@ import {
|
|||||||
SetSize,
|
SetSize,
|
||||||
ResetPosition,
|
ResetPosition,
|
||||||
SetResizable,
|
SetResizable,
|
||||||
SetMovable,
|
|
||||||
ClearWindow,
|
ClearWindow,
|
||||||
CreateWindow,
|
CreateWindow,
|
||||||
|
Top,
|
||||||
} from '@/services/Windows';
|
} from '@/services/Windows';
|
||||||
|
|
||||||
import WindowHandle from './partials/WindowHandle.vue';
|
import WindowHandle from './partials/WindowHandle.vue';
|
||||||
@@ -17,6 +17,7 @@ import { SetUser } from '~/services/User';
|
|||||||
import Spinner from '../partials/Spinner.vue';
|
import Spinner from '../partials/Spinner.vue';
|
||||||
|
|
||||||
const handle = ref(null);
|
const handle = ref(null);
|
||||||
|
const wrapper = ref(null);
|
||||||
|
|
||||||
const props = defineProps(['data']);
|
const props = defineProps(['data']);
|
||||||
const data = props.data;
|
const data = props.data;
|
||||||
@@ -29,6 +30,7 @@ const password = ref("");
|
|||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
Top(wrapper);
|
||||||
SetupHandle(id, handle);
|
SetupHandle(id, handle);
|
||||||
SetSize(id, {width: 450, height: 480});
|
SetSize(id, {width: 450, height: 480});
|
||||||
SetResizable(id, false);
|
SetResizable(id, false);
|
||||||
@@ -37,7 +39,7 @@ onMounted(() => {
|
|||||||
|
|
||||||
function ShowMainMenu(){
|
function ShowMainMenu(){
|
||||||
CreateWindow('main_menu');
|
CreateWindow('main_menu');
|
||||||
ClearWindow('login');
|
ClearWindow({type: 'login'});
|
||||||
}
|
}
|
||||||
|
|
||||||
function login() {
|
function login() {
|
||||||
@@ -61,13 +63,13 @@ function login() {
|
|||||||
|
|
||||||
function toRegister(){
|
function toRegister(){
|
||||||
CreateWindow('register');
|
CreateWindow('register');
|
||||||
ClearWindow('login');
|
ClearWindow({type: 'login'});
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="window-wrapper" :id="'window-wrapper-' + id">
|
<div class="window-wrapper" :id="'window-wrapper-' + id" ref="wrapper">
|
||||||
<WindowHandle :window="id" ref="handle"></WindowHandle>
|
<WindowHandle :window="id" ref="handle"></WindowHandle>
|
||||||
|
|
||||||
<!-- Body -->
|
<!-- Body -->
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { onMounted, ref } from 'vue';
|
import { onMounted, ref } from 'vue';
|
||||||
import { SetupHandle, SetSize, ResetPosition } from '@/services/Windows';
|
import { SetupHandle, SetSize, ResetPosition, Top } from '@/services/Windows';
|
||||||
|
|
||||||
import WindowHandle from './partials/WindowHandle.vue';
|
import WindowHandle from './partials/WindowHandle.vue';
|
||||||
import VersionRender from '../partials/VersionRender.vue';
|
import VersionRender from '../partials/VersionRender.vue';
|
||||||
import EditUserPartial from '../partials/EditUserPartial.vue';
|
import EditUserPartial from '../partials/EditUserPartial.vue';
|
||||||
|
|
||||||
const handle = ref(null);
|
const handle = ref(null);
|
||||||
|
const wrapper = ref(null);
|
||||||
|
|
||||||
const props = defineProps(['data']);
|
const props = defineProps(['data']);
|
||||||
const data = props.data;
|
const data = props.data;
|
||||||
@@ -14,6 +15,7 @@ const data = props.data;
|
|||||||
let id = data.id;
|
let id = data.id;
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
Top(wrapper);
|
||||||
SetupHandle(id, handle);
|
SetupHandle(id, handle);
|
||||||
SetSize(id, {width: 500, height: 460});
|
SetSize(id, {width: 500, height: 460});
|
||||||
ResetPosition(id, "center");
|
ResetPosition(id, "center");
|
||||||
@@ -22,7 +24,7 @@ onMounted(() => {
|
|||||||
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="window-wrapper" :id="'window-wrapper-' + id">
|
<div class="window-wrapper" :id="'window-wrapper-' + id" ref="wrapper">
|
||||||
<WindowHandle :window="id" ref="handle"></WindowHandle>
|
<WindowHandle :window="id" ref="handle"></WindowHandle>
|
||||||
|
|
||||||
<EditUserPartial></EditUserPartial>
|
<EditUserPartial></EditUserPartial>
|
||||||
@@ -33,7 +35,6 @@ onMounted(() => {
|
|||||||
<button class="btn-primary button-expand sound-click" v-on:click="OpenCampaigns" ref="campaignButton">{{ $t("main-menu.campaigns") }}</button>
|
<button class="btn-primary button-expand sound-click" v-on:click="OpenCampaigns" ref="campaignButton">{{ $t("main-menu.campaigns") }}</button>
|
||||||
</div>
|
</div>
|
||||||
<VersionRender></VersionRender>
|
<VersionRender></VersionRender>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { onMounted, ref } from 'vue';
|
import { onMounted, ref } from 'vue';
|
||||||
import { SetupHandle, SetSize, ResetPosition, CreateWindow, ClearWindow } from '@/services/Windows';
|
import { SetupHandle, SetSize, ResetPosition, CreateWindow, ClearWindow, Top } from '@/services/Windows';
|
||||||
|
|
||||||
import WindowHandle from './partials/WindowHandle.vue';
|
import WindowHandle from './partials/WindowHandle.vue';
|
||||||
import Spinner from '../partials/Spinner.vue';
|
import Spinner from '../partials/Spinner.vue';
|
||||||
@@ -9,6 +9,7 @@ import Server from '~/services/Server';
|
|||||||
import { errorMessages } from 'vue/compiler-sfc';
|
import { errorMessages } from 'vue/compiler-sfc';
|
||||||
|
|
||||||
const handle = ref(null);
|
const handle = ref(null);
|
||||||
|
const wrapper = ref(null);
|
||||||
|
|
||||||
const props = defineProps(['data']);
|
const props = defineProps(['data']);
|
||||||
const data = props.data;
|
const data = props.data;
|
||||||
@@ -34,6 +35,7 @@ const images = [
|
|||||||
const splashSource = ref("");
|
const splashSource = ref("");
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
Top(wrapper);
|
||||||
SetupHandle(id, handle);
|
SetupHandle(id, handle);
|
||||||
SetSize(id, {width: 500});
|
SetSize(id, {width: 500});
|
||||||
ResetPosition(id, "center");
|
ResetPosition(id, "center");
|
||||||
@@ -46,7 +48,7 @@ onMounted(() => {
|
|||||||
|
|
||||||
function toLogin(){
|
function toLogin(){
|
||||||
CreateWindow('login');
|
CreateWindow('login');
|
||||||
ClearWindow('register');
|
ClearWindow({type: 'register'});
|
||||||
}
|
}
|
||||||
|
|
||||||
function register(){
|
function register(){
|
||||||
@@ -93,7 +95,7 @@ function register(){
|
|||||||
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="window-wrapper" :id="'window-wrapper-' + id">
|
<div class="window-wrapper" :id="'window-wrapper-' + id" ref="wrapper">
|
||||||
<WindowHandle :window="id" ref="handle"></WindowHandle>
|
<WindowHandle :window="id" ref="handle"></WindowHandle>
|
||||||
|
|
||||||
<!-- Body -->
|
<!-- Body -->
|
||||||
|
|||||||
144
frontend/app/components/windows/SettingsWindow.vue
Normal file
144
frontend/app/components/windows/SettingsWindow.vue
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
<script setup>
|
||||||
|
import { onMounted, ref } from 'vue';
|
||||||
|
|
||||||
|
import WindowHandle from './partials/WindowHandle.vue';
|
||||||
|
import Tabs from '../layouts/Tabs.vue';
|
||||||
|
import Dropdown from '../layouts/Dropdown.vue';
|
||||||
|
import { GetUser, GetUserSetting, SetUserSetting } from '@/services/User';
|
||||||
|
import { SetupHandle, SetSize, ResetPosition, Top, ClearWindow, CreateWindow, SetMinSize, SetResizable } from '@/services/Windows';
|
||||||
|
|
||||||
|
const handle = ref(null);
|
||||||
|
const wrapper = ref(null);
|
||||||
|
|
||||||
|
const props = defineProps(['data']);
|
||||||
|
const data = props.data;
|
||||||
|
|
||||||
|
const id = data.id;
|
||||||
|
|
||||||
|
const rows = ref([{id: "account-settings", value: "settings.tabs.account-settings"}]);
|
||||||
|
|
||||||
|
/* TODO
|
||||||
|
const languageOptions = ref(["English", "Spanish", "Catalan"])
|
||||||
|
const langSelector = ref(null);
|
||||||
|
const currentLanguage = ref("");
|
||||||
|
*/
|
||||||
|
onBeforeMount(() => {
|
||||||
|
/*
|
||||||
|
let codes = {
|
||||||
|
"en-US": "English",
|
||||||
|
"es-ES": "Spanish",
|
||||||
|
"ca": "Catalan"
|
||||||
|
}
|
||||||
|
GetUserSetting('lang').then(value => {
|
||||||
|
currentLanguage.value = codes[value ?? 'en']
|
||||||
|
console.log(currentLanguage.value)
|
||||||
|
});
|
||||||
|
*/
|
||||||
|
if(GetUser().admin) rows.value.push({
|
||||||
|
id: "site-administration",
|
||||||
|
value: "settings.tabs.site-administration"
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
Top(wrapper);
|
||||||
|
SetupHandle(id, handle);
|
||||||
|
SetSize(id, {width: 400, height: 480});
|
||||||
|
ResetPosition(id, "center");
|
||||||
|
|
||||||
|
SetResizable(id, true);
|
||||||
|
SetMinSize(id, {width: 350, height: 280});
|
||||||
|
});
|
||||||
|
|
||||||
|
function OpenManageAccounts(){
|
||||||
|
ClearWindow('settings');
|
||||||
|
CreateWindow('account_management', {
|
||||||
|
type: 'account_management',
|
||||||
|
title: 'settings.site-administration.manage-accounts.title',
|
||||||
|
id: 'account-management',
|
||||||
|
back: () => {
|
||||||
|
ClearWindow('account-management')
|
||||||
|
CreateWindow('settings', {
|
||||||
|
id: 'settings',
|
||||||
|
type: 'settings',
|
||||||
|
title: 'settings.title',
|
||||||
|
back: () => { ClearWindow('settings'); CreateWindow('main_menu'); }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function OpenManagePlugins(){
|
||||||
|
ClearWindow('settings');
|
||||||
|
CreateWindow('plugin_management', {
|
||||||
|
type: 'plugin_management',
|
||||||
|
title: 'settings.site-administration.manage-plugins.title',
|
||||||
|
id: 'plugin-management',
|
||||||
|
back: () => {
|
||||||
|
ClearWindow('plugin-management')
|
||||||
|
CreateWindow('settings', {
|
||||||
|
id: 'settings',
|
||||||
|
type: 'settings',
|
||||||
|
title: 'settings.title',
|
||||||
|
back: () => { ClearWindow('settings'); CreateWindow('main_menu'); }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="window-wrapper" :id="'window-wrapper-' + id" ref="wrapper">
|
||||||
|
<WindowHandle :window="id" ref="handle"></WindowHandle>
|
||||||
|
|
||||||
|
<!-- Body -->
|
||||||
|
<Tabs :rows="rows">
|
||||||
|
<template #account-settings>
|
||||||
|
<div class="form-container">
|
||||||
|
<div class="form-element">
|
||||||
|
<label>{{ $t('settings.account.language') }}</label>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #site-administration>
|
||||||
|
<div class="form-element centered">
|
||||||
|
<button v-on:click.prevent="OpenManageAccounts">{{ $t('settings.site-administration.manage-accounts-button') }}</button>
|
||||||
|
</div>
|
||||||
|
<div class="form-element centered">
|
||||||
|
<button v-on:click.prevent="OpenManagePlugins">{{ $t('settings.site-administration.manage-plugins-button') }}</button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</Tabs>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.window-wrapper {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.splash-image {
|
||||||
|
width: 600px;
|
||||||
|
height: 250px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-field {
|
||||||
|
padding: 10px;
|
||||||
|
display: flex;
|
||||||
|
align-items: left;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: left;
|
||||||
|
width: 600px;
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
@@ -74,13 +74,13 @@ defineExpose({
|
|||||||
<div class="window-handle" :id="'window-handle-' + id">
|
<div class="window-handle" :id="'window-handle-' + id">
|
||||||
|
|
||||||
<div class="left" v-if="def">
|
<div class="left" v-if="def">
|
||||||
<img class="icon icon-add-margin" :src="ArrowLeftIcon" draggable="false" ref="backButton" v-if="hasBack" v-on:click="backFunction">
|
<img class="icon-handle icon-add-margin" :src="ArrowLeftIcon" draggable="false" ref="backButton" v-if="hasBack" v-on:click="backFunction">
|
||||||
</div>
|
</div>
|
||||||
<div class="center" v-if="def">
|
<div class="center" v-if="def">
|
||||||
<span>{{ title }}</span>
|
<span>{{ title }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="right">
|
<div class="right">
|
||||||
<img class="icon" :src="XMarkIcon" draggable="false" ref="closeButton" v-if="close" v-on:click="CloseButton">
|
<img class="icon-handle" :src="XMarkIcon" draggable="false" ref="closeButton" v-if="close" v-on:click="CloseButton">
|
||||||
</div>
|
</div>
|
||||||
<!-- span>{{ title }}</span>
|
<!-- span>{{ title }}</span>
|
||||||
|
|
||||||
@@ -139,4 +139,10 @@ defineExpose({
|
|||||||
background-color: var(--color-window-handle-background);
|
background-color: var(--color-window-handle-background);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.icon-handle {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
filter: invert(var(--color-icon-invert));
|
||||||
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
148
frontend/app/services/ContextMenu.js
Normal file
148
frontend/app/services/ContextMenu.js
Normal file
@@ -0,0 +1,148 @@
|
|||||||
|
// You should hide the context menu when the element that has the
|
||||||
|
// event gets removed
|
||||||
|
|
||||||
|
let margin = -3;
|
||||||
|
|
||||||
|
let cursorX = 0;
|
||||||
|
let cursorY = 0;
|
||||||
|
|
||||||
|
let arrowIcon = "icons/iconoir/regular/nav-arrow-right.svg";
|
||||||
|
|
||||||
|
import { animate } from 'motion'
|
||||||
|
|
||||||
|
function Show(){
|
||||||
|
let contextMenu = document.getElementById('context-menu');
|
||||||
|
contextMenu.style.display = "flex";
|
||||||
|
contextMenu.style.top = (cursorY + margin) + "px";
|
||||||
|
contextMenu.style.left = (cursorX + margin) + "px";
|
||||||
|
}
|
||||||
|
|
||||||
|
function HideContextMenu(){
|
||||||
|
let contextMenu = document.getElementById('context-menu');
|
||||||
|
contextMenu.style.display = "none";
|
||||||
|
}
|
||||||
|
|
||||||
|
function PopulateContext(val){
|
||||||
|
let children = [];
|
||||||
|
|
||||||
|
let elementNum = 0;
|
||||||
|
val.forEach(element => {
|
||||||
|
let contextMenuElement = document.createElement('div');
|
||||||
|
contextMenuElement.classList.add("context-menu-element");
|
||||||
|
if(element.action)
|
||||||
|
contextMenuElement.addEventListener("click", element.action);
|
||||||
|
|
||||||
|
let spanInfo = document.createElement('span');
|
||||||
|
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;
|
||||||
|
contextMenuElement.appendChild(iconContextElement);
|
||||||
|
|
||||||
|
let childContextMenuElement = document.createElement('div');
|
||||||
|
childContextMenuElement.classList.add("context-menu");
|
||||||
|
childContextMenuElement.style.left = "100%";
|
||||||
|
childContextMenuElement.style.top = "0";
|
||||||
|
childContextMenuElement.style.display = "none";
|
||||||
|
|
||||||
|
let childChildren = PopulateContext(element.context);
|
||||||
|
childChildren.forEach((child) => childContextMenuElement.appendChild(child));
|
||||||
|
|
||||||
|
contextMenuElement.addEventListener("mouseenter", () => {
|
||||||
|
childContextMenuElement.style.display = "flex";
|
||||||
|
});
|
||||||
|
|
||||||
|
contextMenuElement.addEventListener("mouseleave", () => {
|
||||||
|
childContextMenuElement.style.display = "none";
|
||||||
|
})
|
||||||
|
|
||||||
|
contextMenuElement.appendChild(childContextMenuElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
children.push(contextMenuElement);
|
||||||
|
|
||||||
|
animate(contextMenuElement, {
|
||||||
|
opacity: [0, 1],
|
||||||
|
translateY: [-20, -2]
|
||||||
|
}, {duration: 0.15}).finished.then(() => {
|
||||||
|
|
||||||
|
});
|
||||||
|
elementNum++;
|
||||||
|
});
|
||||||
|
|
||||||
|
return children;
|
||||||
|
}
|
||||||
|
|
||||||
|
function PopulateContextMenu(val){
|
||||||
|
let contextMenu = document.getElementById('context-menu');
|
||||||
|
let children = PopulateContext(val);
|
||||||
|
|
||||||
|
contextMenu.replaceChildren();
|
||||||
|
children.forEach((el) => contextMenu.appendChild(el));
|
||||||
|
}
|
||||||
|
|
||||||
|
function AddContextMenu(element, val, options = {}){
|
||||||
|
element._dr_context = val;
|
||||||
|
|
||||||
|
function show(e){
|
||||||
|
e.preventDefault();
|
||||||
|
PopulateContextMenu(val);
|
||||||
|
Show();
|
||||||
|
if(options.dropdown){
|
||||||
|
let rect = element.getBoundingClientRect();
|
||||||
|
let contextMenu = document.getElementById('context-menu');
|
||||||
|
contextMenu.style.top = rect.bottom + "px";
|
||||||
|
contextMenu.style.left = rect.left + "px";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
element.addEventListener('contextmenu', show);
|
||||||
|
if(options.dropdown) element.addEventListener('click', show);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function UpdateVisibility(){
|
||||||
|
let contextMenu = document.getElementById('context-menu');
|
||||||
|
let element = document.elementFromPoint(cursorX, cursorY);
|
||||||
|
let mustHide = true;
|
||||||
|
while(element){
|
||||||
|
if(element == contextMenu){
|
||||||
|
mustHide = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
element = element.parentElement;
|
||||||
|
}
|
||||||
|
if(mustHide) HideContextMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
function SetupContextMenu(){
|
||||||
|
HideContextMenu();
|
||||||
|
|
||||||
|
document.addEventListener('mousemove', (e) => {
|
||||||
|
cursorX = e.clientX;
|
||||||
|
cursorY = e.clientY;
|
||||||
|
});
|
||||||
|
|
||||||
|
document.addEventListener('mousedown', UpdateVisibility);
|
||||||
|
}
|
||||||
|
|
||||||
|
function ShowContextMenu(val){
|
||||||
|
PopulateContextMenu(val);
|
||||||
|
Show();
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
SetupContextMenu,
|
||||||
|
AddContextMenu,
|
||||||
|
ShowContextMenu,
|
||||||
|
HideContextMenu
|
||||||
|
};
|
||||||
@@ -1,9 +1,13 @@
|
|||||||
import { ClearWindowsWithType, GetFirstWindowId } from './Windows'
|
import { ClearWindow, GetFirstWindowId } from './Windows'
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Put here all dragonroll windows
|
Put here all dragonroll windows
|
||||||
*/
|
*/
|
||||||
const defWindows = {
|
const defWindows = {
|
||||||
|
example: {
|
||||||
|
title: 'windows.example',
|
||||||
|
component: () => import('~/components/windows/ExampleWindow.vue'),
|
||||||
|
},
|
||||||
login: {
|
login: {
|
||||||
title: 'windows.login',
|
title: 'windows.login',
|
||||||
movable: false,
|
movable: false,
|
||||||
@@ -18,16 +22,18 @@ const defWindows = {
|
|||||||
title: 'windows.main-menu',
|
title: 'windows.main-menu',
|
||||||
component: () => import('~/components/windows/MainMenuWindow.vue'),
|
component: () => import('~/components/windows/MainMenuWindow.vue'),
|
||||||
},
|
},
|
||||||
example: {
|
|
||||||
title: 'windows.example',
|
|
||||||
component: () => import('~/components/windows/ExampleWindow.vue'),
|
|
||||||
},
|
|
||||||
edit_profile: {
|
edit_profile: {
|
||||||
title: "windows.edit-profile",
|
title: "windows.edit-profile",
|
||||||
component: () => import('~/components/windows/EditProfileWindow.vue'),
|
component: () => import('~/components/windows/EditProfileWindow.vue'),
|
||||||
close: () => ClearWindowsWithType(GetFirstWindowId('edit_profile')),
|
close: () => ClearWindow({type: 'edit_profile'}),
|
||||||
movable: true
|
movable: true
|
||||||
},
|
},
|
||||||
|
settings: {
|
||||||
|
title: "windows.settings",
|
||||||
|
component: () => import('~/components/windows/SettingsWindow.vue'),
|
||||||
|
close: () => ClearWindow({type: 'settings'}),
|
||||||
|
movable: true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ let currentId = 0;
|
|||||||
function SetupHandle(id, handle) {
|
function SetupHandle(id, handle) {
|
||||||
|
|
||||||
// Update window info with handle info
|
// Update window info with handle info
|
||||||
console.log(id);
|
|
||||||
let win = GetWindowWithId(id);
|
let win = GetWindowWithId(id);
|
||||||
|
|
||||||
let currentWindowId = "window-wrapper-" + id;
|
let currentWindowId = "window-wrapper-" + id;
|
||||||
@@ -189,7 +188,6 @@ function GetPosition(id) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function ResetPosition(id, pos) {
|
function ResetPosition(id, pos) {
|
||||||
console.log("The id: ", id)
|
|
||||||
let win = GetWindowWithId(id);
|
let win = GetWindowWithId(id);
|
||||||
let data = { x: win.x, y: win.y };
|
let data = { x: win.x, y: win.y };
|
||||||
|
|
||||||
@@ -202,9 +200,6 @@ function ResetPosition(id, pos) {
|
|||||||
|
|
||||||
|
|
||||||
function CreateWindow(type, data = {}) {
|
function CreateWindow(type, data = {}) {
|
||||||
console.log("Creating window")
|
|
||||||
console.log(windows.value);
|
|
||||||
|
|
||||||
let finalData = { ...{ type, id: currentId }, ...defWindows[type], ...data }
|
let finalData = { ...{ type, id: currentId }, ...defWindows[type], ...data }
|
||||||
currentId++;
|
currentId++;
|
||||||
|
|
||||||
@@ -217,25 +212,19 @@ function CreateWindow(type, data = {}) {
|
|||||||
}
|
}
|
||||||
if (!contains) {
|
if (!contains) {
|
||||||
windows.value.push(finalData);
|
windows.value.push(finalData);
|
||||||
|
|
||||||
console.log(windows.value)
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
SetOnTop(finalData.id);
|
|
||||||
if (finalData.create) finalData.create();
|
if (finalData.create) finalData.create();
|
||||||
}, 0);
|
}, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function CreateChildWindow(parentId, type, data = {}) {
|
function CreateChildWindow(parentId, type, data = {}) {
|
||||||
console.log("Creating child window")
|
const newId = currentId;
|
||||||
console.log(parentId, type, data);
|
|
||||||
let finalData = { ...{ type }, ...defWindows[type], ...data }
|
|
||||||
|
|
||||||
let parent = GetWindowWithId(parentId);
|
let parent = GetWindowWithId(parentId);
|
||||||
console.log(parent);
|
if (parent.children) parent.children.push(newId); // We will create the child window right now
|
||||||
if (parent.children) parent.children.push(finalData.type);
|
else parent.children = [newId];
|
||||||
else parent.children = [finalData.type];
|
|
||||||
CreateWindow(type, data);
|
CreateWindow(type, data);
|
||||||
|
console.log(windows.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
function GetFirstWindowId(type) {
|
function GetFirstWindowId(type) {
|
||||||
@@ -250,26 +239,29 @@ function ClearAll() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function ClearWindows(data) {
|
function clearWindowById(id){
|
||||||
for (let i = 0; i < windows.value.length; i++) {
|
|
||||||
ClearWindow(windows.value[i].type);
|
|
||||||
}
|
|
||||||
// reload.value += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
function ClearWindowsWithType(type) {
|
|
||||||
const index = windows.value.findIndex(w => w.type === type);
|
|
||||||
if (index !== -1) ClearWindow(windows.value[index].id)
|
|
||||||
// reload.value += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
function ClearWindow(id) {
|
|
||||||
let win = GetWindowWithId(id);
|
let win = GetWindowWithId(id);
|
||||||
console.log(win);
|
|
||||||
if (!win) return;
|
if (!win) return;
|
||||||
if (win.children) for (let i = 0; i < win.children.length; i++) ClearWindow(win.children[i].id);
|
if (win.children) for (let i = 0; i < win.children.length; i++) clearWindowById(win.children[i]);
|
||||||
const index = windows.value.findIndex(w => w.type === id)
|
const index = windows.value.findIndex(w => w.id === id)
|
||||||
if (index !== -1) windows.value.splice(index, 1)
|
if (index !== -1) windows.value.splice(index, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
function ClearWindow(selector) {
|
||||||
|
if(selector.type !== undefined) {
|
||||||
|
const type = selector.type;
|
||||||
|
for(let i = 0; i < windows.value.length; i++) {
|
||||||
|
if(windows.value[i].type == type) {
|
||||||
|
clearWindowById(windows.value[i].id);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(selector.id !== undefined) {
|
||||||
|
const id = selector.id;
|
||||||
|
clearWindowById(id);
|
||||||
|
}
|
||||||
|
|
||||||
// reload.value += 1;
|
// reload.value += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -300,6 +292,13 @@ function SetOnTop(id) {
|
|||||||
} catch(e) {}
|
} catch(e) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function Top(element) {
|
||||||
|
try {
|
||||||
|
currentIndex += 1;
|
||||||
|
element.value.style.zIndex = currentIndex;
|
||||||
|
} catch(e) {}
|
||||||
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
windows,
|
windows,
|
||||||
SetupHandle,
|
SetupHandle,
|
||||||
@@ -311,7 +310,6 @@ export {
|
|||||||
SetMovable,
|
SetMovable,
|
||||||
ResetPosition,
|
ResetPosition,
|
||||||
WindowMap,
|
WindowMap,
|
||||||
ClearWindows,
|
|
||||||
CreateWindow,
|
CreateWindow,
|
||||||
CreateChildWindow,
|
CreateChildWindow,
|
||||||
GetFirstWindowId,
|
GetFirstWindowId,
|
||||||
@@ -320,7 +318,7 @@ export {
|
|||||||
SaveWindowPos,
|
SaveWindowPos,
|
||||||
GetPosition,
|
GetPosition,
|
||||||
ClearWindow,
|
ClearWindow,
|
||||||
ClearWindowsWithType,
|
|
||||||
ClearAll,
|
ClearAll,
|
||||||
|
Top,
|
||||||
getComponent
|
getComponent
|
||||||
}
|
}
|
||||||
@@ -4,7 +4,8 @@
|
|||||||
"register": "Register",
|
"register": "Register",
|
||||||
"main-menu": "Dragonroll",
|
"main-menu": "Dragonroll",
|
||||||
"example": "Example Window",
|
"example": "Example Window",
|
||||||
"edit-profile": "Edit Profile"
|
"edit-profile": "Edit Profile",
|
||||||
|
"settings": "Settings"
|
||||||
},
|
},
|
||||||
"login": {
|
"login": {
|
||||||
"username": "Username or email",
|
"username": "Username or email",
|
||||||
|
|||||||
Reference in New Issue
Block a user