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 { GetUser, HasAdmin } from './services/User';
|
||||
import TooltipManager from './components/managers/TooltipManager.vue';
|
||||
import ContextMenuManager from './components/managers/ContextMenuManager.vue';
|
||||
|
||||
async function start(){
|
||||
if(GetUser()){
|
||||
@@ -41,6 +42,7 @@ onMounted(() => {
|
||||
<div class="viewer">
|
||||
<ToastManager></ToastManager>
|
||||
<TooltipManager></TooltipManager>
|
||||
<ContextMenuManager></ContextMenuManager>
|
||||
<WindowManager></WindowManager>
|
||||
<ContentManager></ContentManager>
|
||||
<!-- 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(){
|
||||
ClearWindow('main_menu');
|
||||
ClearWindow({type: 'main_menu'});
|
||||
CreateWindow('settings', {
|
||||
id: 'settings',
|
||||
type: 'settings',
|
||||
title: 'settings.title',
|
||||
back: () => { ClearWindow('settings'); CreateWindow('main_menu'); }
|
||||
back: () => { ClearWindow({type: 'settings'}); CreateWindow({type: 'main_menu'}); }
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup>
|
||||
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 { SetMinSize, SetResizable } from '@/services/Windows';
|
||||
@@ -16,12 +16,14 @@ const data = props.data;
|
||||
const userIcon = ref("");
|
||||
|
||||
const handle = ref(null);
|
||||
const wrapper = ref(null);
|
||||
const isAdmin = ref(false);
|
||||
|
||||
let id = data.id;
|
||||
console.log(data);
|
||||
|
||||
onMounted(() => {
|
||||
Top(wrapper);
|
||||
SetupHandle(id, handle);
|
||||
SetSize(id, {width: 500, height: 480});
|
||||
ResetPosition(id, "center");
|
||||
@@ -44,7 +46,7 @@ function RemoveUser(){
|
||||
|
||||
|
||||
<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>
|
||||
|
||||
<BigIconTemplate :title="data.user.username" :img="userIcon">
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
<script setup>
|
||||
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';
|
||||
|
||||
const handle = ref(null);
|
||||
const wrapper = ref(null);
|
||||
|
||||
const props = defineProps(['data']);
|
||||
const data = props.data;
|
||||
@@ -12,6 +13,7 @@ const data = props.data;
|
||||
let id = data.id;
|
||||
|
||||
onMounted(() => {
|
||||
Top(wrapper);
|
||||
SetupHandle(id, handle);
|
||||
SetSize(id, {width: 500, height: 380});
|
||||
ResetPosition(id, "center");
|
||||
@@ -20,7 +22,7 @@ onMounted(() => {
|
||||
|
||||
|
||||
<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>
|
||||
|
||||
<!-- Body -->
|
||||
|
||||
@@ -5,9 +5,9 @@ import {
|
||||
SetSize,
|
||||
ResetPosition,
|
||||
SetResizable,
|
||||
SetMovable,
|
||||
ClearWindow,
|
||||
CreateWindow,
|
||||
Top,
|
||||
} from '@/services/Windows';
|
||||
|
||||
import WindowHandle from './partials/WindowHandle.vue';
|
||||
@@ -17,6 +17,7 @@ import { SetUser } from '~/services/User';
|
||||
import Spinner from '../partials/Spinner.vue';
|
||||
|
||||
const handle = ref(null);
|
||||
const wrapper = ref(null);
|
||||
|
||||
const props = defineProps(['data']);
|
||||
const data = props.data;
|
||||
@@ -29,6 +30,7 @@ const password = ref("");
|
||||
const loading = ref(false);
|
||||
|
||||
onMounted(() => {
|
||||
Top(wrapper);
|
||||
SetupHandle(id, handle);
|
||||
SetSize(id, {width: 450, height: 480});
|
||||
SetResizable(id, false);
|
||||
@@ -37,7 +39,7 @@ onMounted(() => {
|
||||
|
||||
function ShowMainMenu(){
|
||||
CreateWindow('main_menu');
|
||||
ClearWindow('login');
|
||||
ClearWindow({type: 'login'});
|
||||
}
|
||||
|
||||
function login() {
|
||||
@@ -61,13 +63,13 @@ function login() {
|
||||
|
||||
function toRegister(){
|
||||
CreateWindow('register');
|
||||
ClearWindow('login');
|
||||
ClearWindow({type: 'login'});
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<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>
|
||||
|
||||
<!-- Body -->
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
<script setup>
|
||||
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 VersionRender from '../partials/VersionRender.vue';
|
||||
import EditUserPartial from '../partials/EditUserPartial.vue';
|
||||
|
||||
const handle = ref(null);
|
||||
const wrapper = ref(null);
|
||||
|
||||
const props = defineProps(['data']);
|
||||
const data = props.data;
|
||||
@@ -14,6 +15,7 @@ const data = props.data;
|
||||
let id = data.id;
|
||||
|
||||
onMounted(() => {
|
||||
Top(wrapper);
|
||||
SetupHandle(id, handle);
|
||||
SetSize(id, {width: 500, height: 460});
|
||||
ResetPosition(id, "center");
|
||||
@@ -22,7 +24,7 @@ onMounted(() => {
|
||||
|
||||
|
||||
<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>
|
||||
|
||||
<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>
|
||||
</div>
|
||||
<VersionRender></VersionRender>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup>
|
||||
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 Spinner from '../partials/Spinner.vue';
|
||||
@@ -9,6 +9,7 @@ import Server from '~/services/Server';
|
||||
import { errorMessages } from 'vue/compiler-sfc';
|
||||
|
||||
const handle = ref(null);
|
||||
const wrapper = ref(null);
|
||||
|
||||
const props = defineProps(['data']);
|
||||
const data = props.data;
|
||||
@@ -34,6 +35,7 @@ const images = [
|
||||
const splashSource = ref("");
|
||||
|
||||
onMounted(() => {
|
||||
Top(wrapper);
|
||||
SetupHandle(id, handle);
|
||||
SetSize(id, {width: 500});
|
||||
ResetPosition(id, "center");
|
||||
@@ -46,7 +48,7 @@ onMounted(() => {
|
||||
|
||||
function toLogin(){
|
||||
CreateWindow('login');
|
||||
ClearWindow('register');
|
||||
ClearWindow({type: 'register'});
|
||||
}
|
||||
|
||||
function register(){
|
||||
@@ -93,7 +95,7 @@ function register(){
|
||||
|
||||
|
||||
<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>
|
||||
|
||||
<!-- 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="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 class="center" v-if="def">
|
||||
<span>{{ title }}</span>
|
||||
</div>
|
||||
<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>
|
||||
<!-- span>{{ title }}</span>
|
||||
|
||||
@@ -139,4 +139,10 @@ defineExpose({
|
||||
background-color: var(--color-window-handle-background);
|
||||
}
|
||||
|
||||
.icon-handle {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
filter: invert(var(--color-icon-invert));
|
||||
}
|
||||
|
||||
</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
|
||||
*/
|
||||
const defWindows = {
|
||||
example: {
|
||||
title: 'windows.example',
|
||||
component: () => import('~/components/windows/ExampleWindow.vue'),
|
||||
},
|
||||
login: {
|
||||
title: 'windows.login',
|
||||
movable: false,
|
||||
@@ -18,16 +22,18 @@ const defWindows = {
|
||||
title: 'windows.main-menu',
|
||||
component: () => import('~/components/windows/MainMenuWindow.vue'),
|
||||
},
|
||||
example: {
|
||||
title: 'windows.example',
|
||||
component: () => import('~/components/windows/ExampleWindow.vue'),
|
||||
},
|
||||
edit_profile: {
|
||||
title: "windows.edit-profile",
|
||||
component: () => import('~/components/windows/EditProfileWindow.vue'),
|
||||
close: () => ClearWindowsWithType(GetFirstWindowId('edit_profile')),
|
||||
close: () => ClearWindow({type: 'edit_profile'}),
|
||||
movable: true
|
||||
},
|
||||
settings: {
|
||||
title: "windows.settings",
|
||||
component: () => import('~/components/windows/SettingsWindow.vue'),
|
||||
close: () => ClearWindow({type: 'settings'}),
|
||||
movable: true
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
|
||||
@@ -23,7 +23,6 @@ let currentId = 0;
|
||||
function SetupHandle(id, handle) {
|
||||
|
||||
// Update window info with handle info
|
||||
console.log(id);
|
||||
let win = GetWindowWithId(id);
|
||||
|
||||
let currentWindowId = "window-wrapper-" + id;
|
||||
@@ -189,7 +188,6 @@ function GetPosition(id) {
|
||||
}
|
||||
|
||||
function ResetPosition(id, pos) {
|
||||
console.log("The id: ", id)
|
||||
let win = GetWindowWithId(id);
|
||||
let data = { x: win.x, y: win.y };
|
||||
|
||||
@@ -202,9 +200,6 @@ function ResetPosition(id, pos) {
|
||||
|
||||
|
||||
function CreateWindow(type, data = {}) {
|
||||
console.log("Creating window")
|
||||
console.log(windows.value);
|
||||
|
||||
let finalData = { ...{ type, id: currentId }, ...defWindows[type], ...data }
|
||||
currentId++;
|
||||
|
||||
@@ -217,25 +212,19 @@ function CreateWindow(type, data = {}) {
|
||||
}
|
||||
if (!contains) {
|
||||
windows.value.push(finalData);
|
||||
|
||||
console.log(windows.value)
|
||||
setTimeout(() => {
|
||||
SetOnTop(finalData.id);
|
||||
if (finalData.create) finalData.create();
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
|
||||
function CreateChildWindow(parentId, type, data = {}) {
|
||||
console.log("Creating child window")
|
||||
console.log(parentId, type, data);
|
||||
let finalData = { ...{ type }, ...defWindows[type], ...data }
|
||||
|
||||
const newId = currentId;
|
||||
let parent = GetWindowWithId(parentId);
|
||||
console.log(parent);
|
||||
if (parent.children) parent.children.push(finalData.type);
|
||||
else parent.children = [finalData.type];
|
||||
if (parent.children) parent.children.push(newId); // We will create the child window right now
|
||||
else parent.children = [newId];
|
||||
CreateWindow(type, data);
|
||||
console.log(windows.value);
|
||||
}
|
||||
|
||||
function GetFirstWindowId(type) {
|
||||
@@ -250,26 +239,29 @@ function ClearAll() {
|
||||
});
|
||||
}
|
||||
|
||||
function ClearWindows(data) {
|
||||
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) {
|
||||
function clearWindowById(id){
|
||||
let win = GetWindowWithId(id);
|
||||
console.log(win);
|
||||
if (!win) return;
|
||||
if (win.children) for (let i = 0; i < win.children.length; i++) ClearWindow(win.children[i].id);
|
||||
const index = windows.value.findIndex(w => w.type === id)
|
||||
if (win.children) for (let i = 0; i < win.children.length; i++) clearWindowById(win.children[i]);
|
||||
const index = windows.value.findIndex(w => w.id === id)
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -300,6 +292,13 @@ function SetOnTop(id) {
|
||||
} catch(e) {}
|
||||
}
|
||||
|
||||
function Top(element) {
|
||||
try {
|
||||
currentIndex += 1;
|
||||
element.value.style.zIndex = currentIndex;
|
||||
} catch(e) {}
|
||||
}
|
||||
|
||||
export {
|
||||
windows,
|
||||
SetupHandle,
|
||||
@@ -311,7 +310,6 @@ export {
|
||||
SetMovable,
|
||||
ResetPosition,
|
||||
WindowMap,
|
||||
ClearWindows,
|
||||
CreateWindow,
|
||||
CreateChildWindow,
|
||||
GetFirstWindowId,
|
||||
@@ -320,7 +318,7 @@ export {
|
||||
SaveWindowPos,
|
||||
GetPosition,
|
||||
ClearWindow,
|
||||
ClearWindowsWithType,
|
||||
ClearAll,
|
||||
Top,
|
||||
getComponent
|
||||
}
|
||||
@@ -4,7 +4,8 @@
|
||||
"register": "Register",
|
||||
"main-menu": "Dragonroll",
|
||||
"example": "Example Window",
|
||||
"edit-profile": "Edit Profile"
|
||||
"edit-profile": "Edit Profile",
|
||||
"settings": "Settings"
|
||||
},
|
||||
"login": {
|
||||
"username": "Username or email",
|
||||
|
||||
Reference in New Issue
Block a user