Whatever ClearWindow needs to be in json
All checks were successful
Build and Deploy Nuxt / build (push) Successful in 42s

This commit is contained in:
2026-04-26 22:57:29 +02:00
parent 475887420c
commit 2b07cc98a6
20 changed files with 509 additions and 59 deletions

View File

@@ -5,6 +5,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';
async function start(){
if(GetUser()){
@@ -39,6 +40,7 @@ onMounted(() => {
<template>
<div class="viewer">
<ToastManager></ToastManager>
<TooltipManager></TooltipManager>
<WindowManager></WindowManager>
<ContentManager></ContentManager>
<!-- Managers -->

View File

@@ -0,0 +1,41 @@
<script setup>
import { onMounted, watch, ref } from 'vue';
import { GetContentRef, SetupTooltip } from '../../services/Tooltip';
let contentRef = ref("");
onMounted(() => {
SetupTooltip();
let content = GetContentRef();
watch(GetContentRef(), () => {
contentRef.value = GetContentRef().value;
})
});
</script>
<template>
<div id="mouse-tooltip" class="mouse-tooltip">
<div class="document">
<span v-html="contentRef"></span>
</div>
</div>
</template>
<style scoped lang="scss">
.mouse-tooltip {
display: none;
position: absolute;
z-index: 214748364;
background-color: var(--tooltip-background);
padding: 3px 6px 3px 6px;
-webkit-box-shadow: 0px 0px 5px -2px rgba(0,0,0,0.75);
-moz-box-shadow: 0px 0px 5px -2px rgba(0,0,0,0.75);
box-shadow: 0px 0px 5px -2px rgba(0,0,0,0.75);
border: solid 1px var(--color-border);
}
</style>

View File

@@ -1,17 +1,14 @@
<script setup>
import { TransitionGroup } from 'vue'
import { Windows, ReloadRef, WindowMap, getComponent } from '@/services/Windows';
// Gestionem ventanas
const reload = ReloadRef();
const windows = Windows();
import { windows, getComponent } from '@/services/Windows';
</script>
<template>
<div class="window-container" :key="reload">
<TransitionGroup name="window">
<component v-for="win in windows" :is="getComponent(win.type)" :key="win.id" :data="win"></component>
<div class="window-container">
<TransitionGroup name="window" tag="div">
<div v-for="win in windows" :key="win.id">
<component :is="getComponent(win.type)" :data="win" />
</div>
</TransitionGroup>
</div>
</template>

View File

@@ -4,10 +4,9 @@ import { GetUser, LogoutUser } from '@/services/User'
import Server from '@/services/Server'
import { ClearWindows, CreateWindow, CreateChildWindow, ClearWindow } from '../../services/Windows';
import { CreateWindow, CreateChildWindow, ClearWindow, GetFirstWindowId } from '../../services/Windows';
import { backendUrl } from '../../services/BackendURL';
import Spinner from './Spinner.vue';
const emitter = useEmitter();
const loadedIcon = ref(false);
const username = ref("");
@@ -45,13 +44,12 @@ function retrieveAvatar(){
function LogOut(){
LogoutUser();
ClearWindows({type: "main_menu"});
ClearWindow({type: "main_menu"});
CreateWindow('login');
}
function EditProfile(){
console.log("User:"); console.log(GetUser());
CreateChildWindow('main_menu', 'edit_profile', {
CreateChildWindow(GetFirstWindowId('main_menu'), 'edit_profile', {
user: GetUser()
});
}
@@ -104,7 +102,7 @@ onMounted(() => {
<Spinner v-show="!loadedIcon" :size="30"></Spinner>
</div>
<div class="main-user-info">
<b>{{ username }}</b><br>Hola
<b>{{ username }}</b><br>
</div>
<div class="main-user-actions">

View File

@@ -0,0 +1,84 @@
<script setup>
import { onMounted, ref } from 'vue';
import { AddTooltip } from '~/services/Tooltip';
const props = defineProps(['icon', 'action', 'size', 'toggled', 'tooltip']);
let icon = props.icon;
let action = props.action;
let size = props.size;
let toggled = props.toggled;
let tooltip = props.tooltip;
const button = ref(null);
onMounted(() => {
if(tooltip){
AddTooltip(button.value, tooltip);
}
})
</script>
<template>
<div class="icon-button sound-click" :class="size + ' ' + toggled" v-on:click.prevent="action" ref="button">
<img class="icon" draggable="false" :src="icon" :class="size">
</div>
</template>
<style scoped lang="scss">
.icon-button {
height: 32px;
width: 32px;
&.big {
height: 42px;
width: 42px;
}
&.small {
height: 24px;
width: 24px;
}
&.toggled {
filter: invert(0.9);
}
background-color: var(--color-background-soft);
border-radius: 6px;
display: flex;
justify-content: center;
align-items: center;
margin: 2px;
transition: .3s background-color;
border: 1px solid var(--color-border);
}
.icon-button:hover {
background-color: var(--color-background-softer);
}
.icon {
height: 24px;
width: 24px;
pointer-events: none;
&.big {
height: 38px;
width: 38px;
}
&.small {
height: 18px;
width: 18px;
}
}
</style>

View File

@@ -0,0 +1,89 @@
<script setup>
import { onMounted, ref } from 'vue';
import { SetupHandle, SetSize, ResetPosition } from '@/services/Windows';
import Server from '@/services/Server'
import { SetMinSize, SetResizable } from '@/services/Windows';
import { backendUrl } from '@/services/BackendURL';
import { GetUser } from '@/services/User';
import WindowHandle from './partials/WindowHandle.vue';
import BigIconTemplate from './partials/BigIconTemplate.vue';
import FixedBottomButtons from './partials/FixedBottomButtons.vue';
const props = defineProps(['data']);
const data = props.data;
const userIcon = ref("");
const handle = ref(null);
const isAdmin = ref(false);
let id = data.id;
console.log(data);
onMounted(() => {
SetupHandle(id, handle);
SetSize(id, {width: 500, height: 480});
ResetPosition(id, "center");
SetResizable(id, true);
SetMinSize(id, {width: 350, height: 280});
isAdmin.value = GetUser().admin;
Server().get('/user/retrieve-avatar?username=' + data.user.username).then((response) => {
if(response.data.image) userIcon.value = backendUrl + "public/" + response.data.image;
else userIcon.value = "public/img/def-avatar.jpg";
}).catch((err) => console.log("Internal error"));
});
function RemoveUser(){
alert("Remove")
}
</script>
<template>
<div class="window-wrapper" :id="'window-wrapper-' + id">
<WindowHandle :window="id" ref="handle"></WindowHandle>
<BigIconTemplate :title="data.user.username" :img="userIcon">
<div v-if="props.data.editable || isAdmin">
</div>
<div v-else>
</div>
</BigIconTemplate>
<FixedBottomButtons v-if="isAdmin" :remove="RemoveUser"></FixedBottomButtons>
</div>
</template>
<style scoped>
.window-wrapper {
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>

View File

@@ -9,9 +9,7 @@ const handle = ref(null);
const props = defineProps(['data']);
const data = props.data;
let id = data.type;
const test = ref(null)
let id = data.id;
onMounted(() => {
SetupHandle(id, handle);

View File

@@ -21,7 +21,7 @@ const handle = ref(null);
const props = defineProps(['data']);
const data = props.data;
let id = data.type;
let id = data.id;
const username = ref("");
const password = ref("");

View File

@@ -11,9 +11,7 @@ const handle = ref(null);
const props = defineProps(['data']);
const data = props.data;
let id = data.type;
const test = ref(null)
let id = data.id;
onMounted(() => {
SetupHandle(id, handle);

View File

@@ -13,8 +13,7 @@ const handle = ref(null);
const props = defineProps(['data']);
const data = props.data;
let id = data.type;
let id = data.id;
const username = ref("");
const password = ref("");

View File

@@ -0,0 +1,31 @@
<script setup>
import { onMounted, ref, watch } from 'vue';
const props = defineProps(['title', 'img']);
const imgSrc = ref("");
onMounted(() => {
imgSrc.value = props.img;
watch(() => props.img, () => {
imgSrc.value = props.img;
});
})
</script>
<template>
<div class="document centered">
<h1>{{props.title}}</h1>
<img :src="imgSrc" class="big-icon">
<br>
<slot></slot>
</div>
</template>
<style lang="scss" scoped>
.big-icon {
height: 80px;
width: 80px;
border: 1px solid var(--color-border);
margin:auto;
}
</style>

View File

@@ -0,0 +1,29 @@
<script setup>
import IconButton from '~/components/partials/IconButton.vue';
const props = defineProps(['plus', 'edit', 'view', 'remove']);
let plus = props.plus;
let edit = props.edit;
let view = props.view;
let remove = props.remove;
</script>
<template>
<div class="fixed-bottom-buttons">
<IconButton v-show="plus" icon="/icons/iconoir/regular/plus.svg" :action="plus"></IconButton>
<IconButton v-show="edit" icon="/icons/iconoir/regular/edit-pencil.svg" :action="edit"></IconButton>
<IconButton v-show="view" icon="/icons/iconoir/solid/eye.svg" :action="view"></IconButton>
<IconButton v-show="remove" icon="/icons/iconoir/solid/trash.svg" :action="remove"></IconButton>
</div>
</template>
<style lang="scss" scoped>
.fixed-bottom-buttons {
position: absolute;
bottom: 10px;
right: 10px;
z-index: 2;
display: flex;
}
</style>

View File

@@ -1,6 +1,6 @@
<script setup>
import { onMounted, ref, watch } from 'vue';
import { GetWindowWithId, ClearWindow, Windows } from '@/services/Windows';
import { GetWindowWithId } from '@/services/Windows';
import ArrowLeftIcon from '/icons/iconoir/regular/arrow-left.svg';
import XMarkIcon from '/icons/iconoir/regular/xmark.svg';

View File

@@ -0,0 +1,87 @@
import { ref } from 'vue';
import { animate } from 'motion';
let content = ref("");
let margin = 14;
let cursorX = 0;
let cursorY = 0;
let showed = false;
let hided = false;
function ShowTooltip(){
let tooltip = document.getElementById('mouse-tooltip');
tooltip.style.display = "block";
if(!showed){
animate(tooltip, {
opacity: [0, 1],
translateY: [20, 0]
}, {duration: 0.1, ease: 'ease-out'});
showed = true;
hided = false;
}
}
function HideTooltip(){
let tooltip = document.getElementById('mouse-tooltip');
if(!hided){
animate(tooltip, {
opacity: [1, 0],
translateY: [0, 20]
}, {duration: 0.1, ease: 'ease-in'}).finished.then(() => tooltip.style.display = "none")
hided = true;
showed = false;
}
}
function AddTooltip(element, val, data = {}){
element._dr_tooltip = {value: val, ...data};
}
function UpdateVisibilityThread(){
let tooltip = document.getElementById('mouse-tooltip');
let elements = document.elementsFromPoint(cursorX, cursorY);
let visible = false;
for(let i = 0; i < elements.length; i++){
let element = elements[i];
if(element._dr_tooltip){
ShowTooltip();
content.value = element._dr_tooltip.value;
if(element._dr_tooltip.max_width) tooltip.style.maxWidth = element._dr_tooltip.max_width + "px";
else tooltip.style.maxWidth = "none";
visible = true;
break;
}
}
if(!visible) HideTooltip();
setTimeout(UpdateVisibilityThread, 0);
}
function SetupTooltip(){
let tooltip = document.getElementById('mouse-tooltip');
document.addEventListener("mousemove", (event) => {
cursorX = event.clientX;
cursorY = event.clientY;
tooltip.style.top = (cursorY + margin) + "px";
tooltip.style.left = (cursorX + margin) + "px";
});
UpdateVisibilityThread();
}
let GetContentRef = () => content;
export {
SetupTooltip,
GetContentRef,
AddTooltip,
};

View File

@@ -1,7 +1,8 @@
import { ClearWindowsWithType, GetFirstWindowId } from './Windows'
/*
Put here all dragonroll windows
*/
const defWindows = {
login: {
title: 'windows.login',
@@ -20,7 +21,13 @@ const defWindows = {
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')),
movable: true
},
}
export {

View File

@@ -1,14 +1,20 @@
import { ref } from 'vue'
import { defWindows } from './WindowDefinitions';
import { defineAsyncComponent } from 'vue'
const componentCache = {}
const getComponent = (type) => {
if (!componentCache[type]) {
componentCache[type] = defineAsyncComponent(
defWindows[type].component
)
}
return componentCache[type]
}
const windows = ref([]);
const getComponent = (type) => defineAsyncComponent(defWindows[type]?.component)
const reload = ref(0);
let ReloadRef = () => { return reload };
let Windows = () => { return windows };
let WindowMap = () => { return windowMap };
let currentIndex = 10;
@@ -107,8 +113,9 @@ function SetupHandle(id, handle) {
// Should move eventually?
window.addEventListener('resize', (event) => {
if(!win.movable){
ResetPosition(id, "center");
for(const w of windows.value){
if(w.movable) continue;
ResetPosition(w.id, "center");
}
})
@@ -182,6 +189,7 @@ function GetPosition(id) {
}
function ResetPosition(id, pos) {
console.log("The id: ", id)
let win = GetWindowWithId(id);
let data = { x: win.x, y: win.y };
@@ -194,42 +202,48 @@ function ResetPosition(id, pos) {
function CreateWindow(type, data = {}) {
console.log("Creating window")
console.log(windows.value);
let finalData = { ...{ type, id: currentId }, ...defWindows[type], ...data }
console.log(finalData);
currentId++;
let contains = false;
for (let i = 0; i < windows.value.length; i++) {
if (windows.value[i].type == finalData.type) {
contains = true;
console.log("It contains")
break;
}
}
if (!contains) {
windows.value.push(finalData);
currentId++;
console.log(finalData);
console.log("Pushed ", finalData.type);
// reload.value += 1;
console.log(windows.value);
console.log(windows.value)
setTimeout(() => {
SetOnTop(finalData.type);
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 }
let parent = GetWindowWithId(parentId);
console.log(parent);
if (parent.children) parent.children.push(finalData.type);
else parent.children = [finalData.type];
CreateWindow(type, data);
}
function GetFirstWindowId(type) {
for (let i = 0; i < windows.value.length; i++) {
if (windows.value[i].type == type) return windows.value[i].id;
}
}
function ClearAll() {
Object.keys(windows).forEach((key) => {
windows.value = [];
@@ -243,21 +257,25 @@ function ClearWindows(data) {
// 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);
console.log(win);
if (!win) return;
if (win.children) for (let i = 0; i < win.children.length; i++) ClearWindow(win.children[i]);
windows.value = windows.value.filter((e) => { return e.type !== id });
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 (index !== -1) windows.value.splice(index, 1)
// reload.value += 1;
}
function GetWindowWithId(id) {
for (let i = 0; i < windows.value.length; i++) {
if (windows.value[i].type == id) {
return windows.value[i];
}
}
const index = windows.value.findIndex(w => w.id === id);
if (index !== -1) return windows.value[index];
}
function CallWindow(id, callableName, arg) {
@@ -283,6 +301,7 @@ function SetOnTop(id) {
}
export {
windows,
SetupHandle,
SetSize,
SetResizable,
@@ -291,17 +310,17 @@ export {
SetPosition,
SetMovable,
ResetPosition,
Windows,
WindowMap,
ReloadRef,
ClearWindows,
CreateWindow,
CreateChildWindow,
GetFirstWindowId,
CallWindow,
GetWindowWithId,
SaveWindowPos,
GetPosition,
ClearWindow,
ClearWindowsWithType,
ClearAll,
getComponent
}