Dynamic icon picker and started item creation prompt
This commit is contained in:
parent
22d093febc
commit
f22be8d6f2
24772
client/public/data/icons.json
Normal file
24772
client/public/data/icons.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -12,31 +12,38 @@ const emitter = useEmitter();
|
|||||||
import { DisplayToast, SetEmitter } from './services/Dragonroll'
|
import { DisplayToast, SetEmitter } from './services/Dragonroll'
|
||||||
import { ImportModule, GetModulesToLoad } from './services/Modules'
|
import { ImportModule, GetModulesToLoad } from './services/Modules'
|
||||||
import { CreateWindow } from './services/Windows';
|
import { CreateWindow } from './services/Windows';
|
||||||
|
import { FetchVanillaResources } from './services/Resources';
|
||||||
|
|
||||||
console.clear();
|
console.clear();
|
||||||
console.log("%cLoaded!!!", "color: #22ff22; font-size: 24px");
|
console.log("%cLoaded!!!", "color: #22ff22; font-size: 24px");
|
||||||
LoadUser();
|
LoadUser();
|
||||||
|
|
||||||
SetEmitter(emitter);
|
SetEmitter(emitter);
|
||||||
|
|
||||||
|
async function preloadModules(){
|
||||||
|
if(GetUser()){
|
||||||
|
CreateWindow('main_menu');
|
||||||
|
// DisplayToast('green', 'Logged in successfully as ' + GetUser().username + '!', 3000)
|
||||||
|
} else CreateWindow('login');
|
||||||
|
|
||||||
|
const modules = GetModulesToLoad();
|
||||||
|
let moduleLoads = [];
|
||||||
|
|
||||||
|
modules.forEach(moduleName => {
|
||||||
|
moduleLoads.push(ImportModule(moduleName));
|
||||||
|
});
|
||||||
|
|
||||||
|
await Promise.all(moduleLoads);
|
||||||
|
|
||||||
|
await FetchVanillaResources();
|
||||||
|
|
||||||
|
DisplayToast('aqua', 'All modules loaded successfully');
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
async function preloadModules(){
|
|
||||||
const modules = GetModulesToLoad();
|
|
||||||
let moduleLoads = [];
|
|
||||||
|
|
||||||
modules.forEach(moduleName => {
|
|
||||||
moduleLoads.push(ImportModule(moduleName));
|
|
||||||
});
|
|
||||||
|
|
||||||
await Promise.all(moduleLoads);
|
|
||||||
DisplayToast('aqua', 'All modules loaded successfully');
|
|
||||||
|
|
||||||
if(GetUser()){
|
|
||||||
CreateWindow('main_menu');
|
|
||||||
DisplayToast('green', 'Logged in successfully as ' + GetUser().username + '!', 3000)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
CreateWindow('login');
|
|
||||||
}
|
|
||||||
|
|
||||||
preloadModules();
|
preloadModules();
|
||||||
})
|
})
|
||||||
|
@ -53,6 +53,13 @@
|
|||||||
--c-gradient-col-3: #0CF25D;
|
--c-gradient-col-3: #0CF25D;
|
||||||
|
|
||||||
--c-golden-border: #AA9E89aa;
|
--c-golden-border: #AA9E89aa;
|
||||||
|
|
||||||
|
--color-common: var(--color-text);
|
||||||
|
--color-uncommon: #27AE60;
|
||||||
|
--color-rare: #3a7eac;
|
||||||
|
--color-very-rare: #b43876;
|
||||||
|
--color-legendary: #f1a90f;
|
||||||
|
--color-artifact: #c73e43;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* semantic color variables for this project */
|
/* semantic color variables for this project */
|
||||||
|
@ -202,5 +202,47 @@ button:active {
|
|||||||
.text-icon {
|
.text-icon {
|
||||||
height: 18px;
|
height: 18px;
|
||||||
width: 18px;
|
width: 18px;
|
||||||
margin-right: 5px;
|
margin-bottom: -4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.invert {
|
||||||
|
filter: invert(0.9);
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-container {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
span.important {
|
||||||
|
font-family: NodestoCapsCondensed;
|
||||||
|
font-size: 24px;
|
||||||
|
line-height: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
span.common {
|
||||||
|
color: var(--color-common);
|
||||||
|
}
|
||||||
|
span.uncommon {
|
||||||
|
color: var(--color-uncommon);
|
||||||
|
}
|
||||||
|
span.rare {
|
||||||
|
color: var(--color-rare);
|
||||||
|
}
|
||||||
|
span.very-rare {
|
||||||
|
color: var(--color-very-rare);
|
||||||
|
}
|
||||||
|
span.legendary {
|
||||||
|
color: var(--color-legendary);
|
||||||
|
}
|
||||||
|
span.artifact {
|
||||||
|
color: var(--color-artifact);
|
||||||
}
|
}
|
@ -8,7 +8,7 @@ let cursorY = 0;
|
|||||||
|
|
||||||
let arrowIcon = "icons/iconoir/regular/nav-arrow-right.svg";
|
let arrowIcon = "icons/iconoir/regular/nav-arrow-right.svg";
|
||||||
|
|
||||||
function ShowContextMenu(){
|
function Show(){
|
||||||
let contextMenu = document.getElementById('context-menu');
|
let contextMenu = document.getElementById('context-menu');
|
||||||
contextMenu.style.display = "flex";
|
contextMenu.style.display = "flex";
|
||||||
contextMenu.style.top = (cursorY + margin) + "px";
|
contextMenu.style.top = (cursorY + margin) + "px";
|
||||||
@ -29,7 +29,7 @@ function PopulateContext(val){
|
|||||||
contextMenuElement.addEventListener("click", element.action);
|
contextMenuElement.addEventListener("click", element.action);
|
||||||
|
|
||||||
let spanInfo = document.createElement('span');
|
let spanInfo = document.createElement('span');
|
||||||
spanInfo.innerText = element.name;
|
spanInfo.innerHTML = element.name;
|
||||||
contextMenuElement.appendChild(spanInfo);
|
contextMenuElement.appendChild(spanInfo);
|
||||||
|
|
||||||
if(element.context){
|
if(element.context){
|
||||||
@ -76,12 +76,12 @@ function AddContextMenu(element, val){
|
|||||||
element.addEventListener('contextmenu', (e) => {
|
element.addEventListener('contextmenu', (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
PopulateContextMenu(val);
|
PopulateContextMenu(val);
|
||||||
ShowContextMenu();
|
Show();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function UpdateVisibility(){
|
function UpdateVisibility(){
|
||||||
let contextMenu = document.getElementById('context-menu');
|
let contextMenu = document.getElementById('context-menu');
|
||||||
let element = document.elementFromPoint(cursorX, cursorY);
|
let element = document.elementFromPoint(cursorX, cursorY);
|
||||||
@ -107,6 +107,11 @@ function SetupContextMenu(){
|
|||||||
document.addEventListener('mousedown', UpdateVisibility);
|
document.addEventListener('mousedown', UpdateVisibility);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function ShowContextMenu(val){
|
||||||
|
PopulateContextMenu(val);
|
||||||
|
Show();
|
||||||
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
SetupContextMenu,
|
SetupContextMenu,
|
||||||
AddContextMenu,
|
AddContextMenu,
|
||||||
|
20
client/src/services/Resources.js
Normal file
20
client/src/services/Resources.js
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
let icons = [];
|
||||||
|
|
||||||
|
async function GetJson(url){
|
||||||
|
let obj = await (await fetch(url)).json();
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function FetchVanillaResources(){
|
||||||
|
icons = (await GetJson('/data/icons.json')).files;
|
||||||
|
}
|
||||||
|
|
||||||
|
function GetIcons(){
|
||||||
|
return icons;
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
FetchVanillaResources,
|
||||||
|
|
||||||
|
GetIcons,
|
||||||
|
};
|
@ -8,7 +8,7 @@ const defValues = {
|
|||||||
'test': {
|
'test': {
|
||||||
id: "example",
|
id: "example",
|
||||||
title: "Example",
|
title: "Example",
|
||||||
close: true
|
close: () => ClearWindow('example')
|
||||||
},
|
},
|
||||||
'login': {
|
'login': {
|
||||||
id: 'login',
|
id: 'login',
|
||||||
@ -28,17 +28,17 @@ const defValues = {
|
|||||||
'welcome': {
|
'welcome': {
|
||||||
id: 'welcome',
|
id: 'welcome',
|
||||||
title: "Welcome",
|
title: "Welcome",
|
||||||
close: true
|
close: () => ClearWindow('welcome')
|
||||||
},
|
},
|
||||||
'edit_profile': {
|
'edit_profile': {
|
||||||
id: 'edit_profile',
|
id: 'edit_profile',
|
||||||
title: "Edit Profile",
|
title: "Edit Profile",
|
||||||
close: true
|
close: () => ClearWindow('edit_profile')
|
||||||
},
|
},
|
||||||
'account_settings': {
|
'account_settings': {
|
||||||
id: 'account_settings',
|
id: 'account_settings',
|
||||||
title: "Dragonroll settings",
|
title: "Dragonroll settings",
|
||||||
close: true
|
close: () => ClearWindow('account_settings')
|
||||||
},
|
},
|
||||||
'campaign_list': {
|
'campaign_list': {
|
||||||
id: 'campaign_list',
|
id: 'campaign_list',
|
||||||
@ -52,13 +52,13 @@ const defValues = {
|
|||||||
id: 'new_campaign',
|
id: 'new_campaign',
|
||||||
title: 'Create campaign',
|
title: 'Create campaign',
|
||||||
parent: 'campaign_list',
|
parent: 'campaign_list',
|
||||||
close: true
|
close: () => ClearWindow('new_campaign')
|
||||||
},
|
},
|
||||||
'join_campaign': {
|
'join_campaign': {
|
||||||
id: 'join_campaign',
|
id: 'join_campaign',
|
||||||
title: 'Join campaign',
|
title: 'Join campaign',
|
||||||
parent: 'campaign_list',
|
parent: 'campaign_list',
|
||||||
close: true
|
close: () => ClearWindow('join_campaign')
|
||||||
},
|
},
|
||||||
'campaign_preview': {
|
'campaign_preview': {
|
||||||
id: 'campaign_preview',
|
id: 'campaign_preview',
|
||||||
@ -72,57 +72,57 @@ const defValues = {
|
|||||||
'chat': {
|
'chat': {
|
||||||
id: 'chat',
|
id: 'chat',
|
||||||
title: 'Chat',
|
title: 'Chat',
|
||||||
close: true
|
close: () => ClearWindow('chat')
|
||||||
},
|
},
|
||||||
'dice_menu': {
|
'dice_menu': {
|
||||||
id: 'dice_menu',
|
id: 'dice_menu',
|
||||||
title: 'Dice roll',
|
title: 'Dice roll',
|
||||||
close: true
|
close: () => ClearWindow('dice_menu')
|
||||||
},
|
},
|
||||||
'map_buttons': {
|
'map_buttons': {
|
||||||
id: 'map_buttons',
|
id: 'map_buttons',
|
||||||
title: '',
|
title: '',
|
||||||
close: true
|
close: () => ClearWindow('map_buttons')
|
||||||
},
|
},
|
||||||
'environment': {
|
'environment': {
|
||||||
id: 'environment',
|
id: 'environment',
|
||||||
title: 'Edit environment',
|
title: 'Edit environment',
|
||||||
close: true
|
close: () => ClearWindow('environment')
|
||||||
},
|
},
|
||||||
'system_selector': {
|
'system_selector': {
|
||||||
id: 'system-selector',
|
id: 'system-selector',
|
||||||
title: "Select a game system",
|
title: "Select a game system",
|
||||||
close: true
|
close: () => ClearWindow('system-selector')
|
||||||
},
|
},
|
||||||
'map_window': {
|
'map_window': {
|
||||||
id: 'map_window',
|
id: 'map_window',
|
||||||
title: 'Maps',
|
title: 'Maps',
|
||||||
close: true
|
close: () => ClearWindow('map_window')
|
||||||
},
|
},
|
||||||
'combat_window': {
|
'combat_window': {
|
||||||
id: 'combat_window',
|
id: 'combat_window',
|
||||||
title: "Combat",
|
title: "Combat",
|
||||||
close: true
|
close: () => ClearWindow('combat_window')
|
||||||
},
|
},
|
||||||
'entity_window': {
|
'entity_window': {
|
||||||
id: 'entity_window',
|
id: 'entity_window',
|
||||||
title: "Entities",
|
title: "Entities",
|
||||||
close: true
|
close: () => ClearWindow('entity_window')
|
||||||
},
|
},
|
||||||
'characters_window': {
|
'characters_window': {
|
||||||
id: 'characters_window',
|
id: 'characters_window',
|
||||||
title: "Characters",
|
title: "Characters",
|
||||||
close: true
|
close: () => ClearWindow('characters_window')
|
||||||
},
|
},
|
||||||
'compendium_window': {
|
'compendium_window': {
|
||||||
id: 'compendium_window',
|
id: 'compendium_window',
|
||||||
title: "Compendium",
|
title: "Compendium",
|
||||||
close: true
|
close: () => ClearWindow('compendium_window')
|
||||||
},
|
},
|
||||||
'character_sheet': {
|
'character_sheet': {
|
||||||
id: 'character_sheet',
|
id: 'character_sheet',
|
||||||
title: 'Character Sheet',
|
title: 'Character Sheet',
|
||||||
close: "true"
|
close: () => ClearWindow('character_sheet')
|
||||||
},
|
},
|
||||||
'book_anvil_window': {
|
'book_anvil_window': {
|
||||||
id: 'book_anvil_window',
|
id: 'book_anvil_window',
|
||||||
@ -132,6 +132,15 @@ const defValues = {
|
|||||||
CreateWindow('main_menu');
|
CreateWindow('main_menu');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
'icon_selector': {
|
||||||
|
id: 'icon-selector',
|
||||||
|
title: "Select an Icon",
|
||||||
|
},
|
||||||
|
'database': {
|
||||||
|
id: 'database',
|
||||||
|
title: "Database",
|
||||||
|
close: () => ClearWindow('database')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const reload = ref(0);
|
const reload = ref(0);
|
||||||
@ -366,6 +375,11 @@ function GetWindowWithId(id){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function CallWindow(id, callableName, arg){
|
||||||
|
let win = GetWindowWithId(id);
|
||||||
|
win[callableName](arg);
|
||||||
|
}
|
||||||
|
|
||||||
function SaveWindowPos(data){
|
function SaveWindowPos(data){
|
||||||
let win = GetWindowWithId(data.id);
|
let win = GetWindowWithId(data.id);
|
||||||
if(win === undefined) return;
|
if(win === undefined) return;
|
||||||
@ -394,6 +408,7 @@ export {
|
|||||||
ClearWindows,
|
ClearWindows,
|
||||||
CreateWindow,
|
CreateWindow,
|
||||||
CreateChildWindow,
|
CreateChildWindow,
|
||||||
|
CallWindow,
|
||||||
GetWindowWithId,
|
GetWindowWithId,
|
||||||
SaveWindowPos,
|
SaveWindowPos,
|
||||||
GetPosition,
|
GetPosition,
|
||||||
|
@ -5,14 +5,20 @@ import IconButton from '../partials/game/IconButton.vue';
|
|||||||
import { AddSound } from '../../services/Sound';
|
import { AddSound } from '../../services/Sound';
|
||||||
import TileMap from './TileMap.vue';
|
import TileMap from './TileMap.vue';
|
||||||
import { DisplayCampaign, GetCampaign, GetClient } from '../../services/Dragonroll';
|
import { DisplayCampaign, GetCampaign, GetClient } from '../../services/Dragonroll';
|
||||||
import { ClearAll, CreateWindow } from '../../services/Windows';
|
import { ClearAll, ClearWindow, CreateWindow } from '../../services/Windows';
|
||||||
|
|
||||||
const game = ref(null);
|
const game = ref(null);
|
||||||
const in_game = InGameRef();
|
const in_game = InGameRef();
|
||||||
const is_dm = ref(false);
|
const is_dm = ref(false);
|
||||||
|
|
||||||
function OpenCampaignPreview(){
|
function OpenCampaignPreview(){
|
||||||
CreateWindow('campaign_preview', {campaign: GetCampaign(), style: 'compact', hide_start: true, back: undefined, close: true});
|
CreateWindow('campaign_preview', {
|
||||||
|
campaign: GetCampaign(),
|
||||||
|
style: 'compact',
|
||||||
|
hide_start: true,
|
||||||
|
back: undefined,
|
||||||
|
close: () => ClearWindow('campaign_preview')
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function OpenChat(){
|
function OpenChat(){
|
||||||
@ -47,8 +53,8 @@ function OpenCharactersWindow(){
|
|||||||
CreateWindow('characters_window');
|
CreateWindow('characters_window');
|
||||||
}
|
}
|
||||||
|
|
||||||
function OpenCompendiumWindow(){
|
function OpenDatabaseWindow(){
|
||||||
CreateWindow('compendium_window');
|
CreateWindow('database');
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(game, () => {
|
watch(game, () => {
|
||||||
@ -83,7 +89,7 @@ watch(game, () => {
|
|||||||
|
|
||||||
<div class="horizontal-button">
|
<div class="horizontal-button">
|
||||||
<IconButton icon="icons/iconoir/regular/group.svg" :action="OpenCharactersWindow"></IconButton>
|
<IconButton icon="icons/iconoir/regular/group.svg" :action="OpenCharactersWindow"></IconButton>
|
||||||
<IconButton icon="icons/iconoir/regular/bookmark-book.svg" :action="OpenCompendiumWindow"></IconButton>
|
<IconButton icon="icons/iconoir/regular/bookmark-book.svg" :action="OpenDatabaseWindow"></IconButton>
|
||||||
<IconButton icon="icons/iconoir/regular/chat-bubble.svg" :action="OpenChat"></IconButton>
|
<IconButton icon="icons/iconoir/regular/chat-bubble.svg" :action="OpenChat"></IconButton>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -29,6 +29,8 @@ import CharacterSheet from '../windows/game/dnd-5e/CharacterSheet.vue'
|
|||||||
import WelcomeWindow from '../windows/WelcomeWindow.vue'
|
import WelcomeWindow from '../windows/WelcomeWindow.vue'
|
||||||
import CompendiumWindow from '../windows/CompendiumWindow.vue'
|
import CompendiumWindow from '../windows/CompendiumWindow.vue'
|
||||||
import BookAnvilWindow from '../windows/BookAnvilWindow.vue'
|
import BookAnvilWindow from '../windows/BookAnvilWindow.vue'
|
||||||
|
import IconSelectorWindow from '../windows/selectors/IconSelectorWindow.vue'
|
||||||
|
import DatabaseWindow from '../windows/game/DatabaseWindow.vue'
|
||||||
|
|
||||||
|
|
||||||
// Gestionem ventanas
|
// Gestionem ventanas
|
||||||
@ -58,13 +60,16 @@ let WindowMap = {
|
|||||||
characters_window: CharactersWindow,
|
characters_window: CharactersWindow,
|
||||||
compendium_window: CompendiumWindow,
|
compendium_window: CompendiumWindow,
|
||||||
book_anvil_window: BookAnvilWindow,
|
book_anvil_window: BookAnvilWindow,
|
||||||
|
icon_selector: IconSelectorWindow,
|
||||||
|
database: DatabaseWindow
|
||||||
};
|
};
|
||||||
|
|
||||||
async function InjectSystemWindows(system){
|
async function InjectSystemWindows(system){
|
||||||
// Hack
|
// Hack
|
||||||
let systemWidows = {
|
let systemWidows = {
|
||||||
character_sheet: (await import(`../windows/game/${system}/CharacterSheet.vue`)).default,
|
character_sheet: (await import(`../windows/game/${system}/CharacterSheet.vue`)).default,
|
||||||
concept_sheet: (await import(`../windows/game/${system}/ConceptSheet.vue`)).default
|
item_sheet: (await import(`../windows/game/${system}/ItemSheet.vue`)).default,
|
||||||
|
create_item_prompt: (await import(`../windows/game/${system}/CreateItemPrompt.vue`)).default,
|
||||||
};
|
};
|
||||||
|
|
||||||
WindowMap = {...WindowMap, ...systemWidows};
|
WindowMap = {...WindowMap, ...systemWidows};
|
||||||
|
49
client/src/views/partials/IconSelector.vue
Normal file
49
client/src/views/partials/IconSelector.vue
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
<script setup>
|
||||||
|
|
||||||
|
import { onMounted, ref, getCurrentInstance, defineExpose } from 'vue';
|
||||||
|
import { ClearWindow, CreateChildWindow } from '../../services/Windows';
|
||||||
|
|
||||||
|
const image = ref(null);
|
||||||
|
const props = defineProps(['window']);
|
||||||
|
|
||||||
|
const uuid = getCurrentInstance().uid;
|
||||||
|
const icon = ref(null);
|
||||||
|
|
||||||
|
function SelectIcon(){
|
||||||
|
CreateChildWindow(props.window, 'icon_selector', {
|
||||||
|
id: 'icon-selector-' + uuid,
|
||||||
|
done: (res) => {
|
||||||
|
icon.value = res;
|
||||||
|
console.log(res);
|
||||||
|
image.value.src = res.selected.path;
|
||||||
|
ClearWindow('icon-selector-' + uuid);
|
||||||
|
},
|
||||||
|
close: () => {
|
||||||
|
ClearWindow('icon-selector-' + uuid);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
icon
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="icon-selector">
|
||||||
|
<img ref="image" src="icons/sundries/books/book-red-exclamation.webp" v-on:click.prevent="SelectIcon">
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.icon-selector {
|
||||||
|
img {
|
||||||
|
border: 1px solid var(--color-border);
|
||||||
|
width: 64px;
|
||||||
|
height: 64px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -18,12 +18,17 @@ const hasBack = ref(false);
|
|||||||
const def = ref(true);
|
const def = ref(true);
|
||||||
const resizable = ref(false);
|
const resizable = ref(false);
|
||||||
|
|
||||||
|
let closeAction;
|
||||||
|
|
||||||
let backFunction;
|
let backFunction;
|
||||||
|
|
||||||
function setupHandle() {
|
function setupHandle() {
|
||||||
let win = GetWindowWithId(id);
|
let win = GetWindowWithId(id);
|
||||||
if(win.title) title.value = win.title;
|
if(win.title) title.value = win.title;
|
||||||
if(win.close) close.value = true;
|
if(win.close){
|
||||||
|
close.value = true;
|
||||||
|
closeAction = win.close;
|
||||||
|
}
|
||||||
if(win.back) {
|
if(win.back) {
|
||||||
hasBack.value = true;
|
hasBack.value = true;
|
||||||
backFunction = win.back;
|
backFunction = win.back;
|
||||||
@ -46,7 +51,8 @@ function CloseButton(){
|
|||||||
const audio = new Audio('/sounds/close.wav');
|
const audio = new Audio('/sounds/close.wav');
|
||||||
audio.type = "audio/wav"
|
audio.type = "audio/wav"
|
||||||
audio.play();
|
audio.play();
|
||||||
ClearWindow(id)
|
if(typeof closeAction === 'function') closeAction();
|
||||||
|
// ClearWindow(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
@ -4,10 +4,12 @@ import WindowHandle from '@/views/partials/WindowHandle.vue';
|
|||||||
import { onMounted, ref } from 'vue';
|
import { onMounted, ref } from 'vue';
|
||||||
import { SetupHandle, SetSize, ResetPosition } from '@/services/Windows';
|
import { SetupHandle, SetSize, ResetPosition } from '@/services/Windows';
|
||||||
import { SetMinSize, SetMaxSize, SetResizable } from '../../services/Windows';
|
import { SetMinSize, SetMaxSize, SetResizable } from '../../services/Windows';
|
||||||
|
import IconSelector from '../partials/IconSelector.vue';
|
||||||
const props = defineProps(['data']);
|
const props = defineProps(['data']);
|
||||||
const data = props.data;
|
const data = props.data;
|
||||||
|
|
||||||
const handle = ref(null);
|
const handle = ref(null);
|
||||||
|
const bookIcon = ref(null);
|
||||||
|
|
||||||
let id = data.id;
|
let id = data.id;
|
||||||
|
|
||||||
@ -28,6 +30,7 @@ onMounted(() => {
|
|||||||
|
|
||||||
<div class="book-anvil-container">
|
<div class="book-anvil-container">
|
||||||
<div class="book-anvil-header">
|
<div class="book-anvil-header">
|
||||||
|
<IconSelector :window="id" ref="bookIcon"></IconSelector>
|
||||||
<img class="img-selector"> <!-- TODO: Canviar això per un component ben fet -->
|
<img class="img-selector"> <!-- TODO: Canviar això per un component ben fet -->
|
||||||
<div class="book-info">
|
<div class="book-info">
|
||||||
<h1>New book</h1>
|
<h1>New book</h1>
|
||||||
@ -39,10 +42,6 @@ onMounted(() => {
|
|||||||
|
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.img-selector {
|
|
||||||
width: 72px;
|
|
||||||
height: 72px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.book-anvil-header {
|
.book-anvil-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
82
client/src/views/windows/game/DatabaseWindow.vue
Normal file
82
client/src/views/windows/game/DatabaseWindow.vue
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
<script setup>
|
||||||
|
import WindowHandle from '@/views/partials/WindowHandle.vue';
|
||||||
|
|
||||||
|
import { onMounted, ref } from 'vue';
|
||||||
|
import { ClearWindow, CreateWindow, ResetPosition, SetSize, SetupHandle } from '../../../services/Windows';
|
||||||
|
import IconButton from '@/views/partials/game/IconButton.vue'
|
||||||
|
|
||||||
|
const handle = ref(null);
|
||||||
|
|
||||||
|
const props = defineProps(['data']);
|
||||||
|
const data = props.data;
|
||||||
|
|
||||||
|
let id = data.id;
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
SetupHandle(id, handle);
|
||||||
|
SetSize(id, {width: 700, height: 800});
|
||||||
|
ResetPosition(id, "center");
|
||||||
|
});
|
||||||
|
|
||||||
|
function OpenCreateItemPrompt(){
|
||||||
|
CreateWindow('create_item_prompt', {id: 'create_item_prompt', title: 'Create Item', close: () => ClearWindow('create_item_prompt')})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="window-wrapper" :id="'window-wrapper-' + id">
|
||||||
|
<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>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="fixed-bottom-buttons">
|
||||||
|
<IconButton icon="icons/iconoir/regular/plus.svg" :action="OpenCreateItemPrompt"></IconButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.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;
|
||||||
|
right: 10px;
|
||||||
|
z-index: 2;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.window-wrapper {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
</style>
|
@ -1,48 +0,0 @@
|
|||||||
<script setup>
|
|
||||||
import WindowHandle from '@/views/partials/WindowHandle.vue';
|
|
||||||
|
|
||||||
import { onMounted, ref } from 'vue';
|
|
||||||
import { SetupHandle, SetSize, ResetPosition } from '@/services/Windows';
|
|
||||||
const props = defineProps(['data']);
|
|
||||||
const data = props.data;
|
|
||||||
|
|
||||||
const handle = ref(null);
|
|
||||||
let id = data.id;
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
SetupHandle(id, handle);
|
|
||||||
SetSize(id, {width: 700, height: 850});
|
|
||||||
ResetPosition(id, "center");
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div class="window-wrapper" :id="'window-wrapper-' + id">
|
|
||||||
<WindowHandle :window="id" ref="handle" handleHeight="105px" custom="true" class="character-sheet-handle"></WindowHandle>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
|
|
||||||
<style scoped lang="scss">
|
|
||||||
@mixin shadow {
|
|
||||||
-moz-box-shadow: 0px 0px 10px -1px rgba(0,0,0,0.25);
|
|
||||||
box-shadow: 0px 0px 10px -1px rgba(0,0,0,0.25);
|
|
||||||
}
|
|
||||||
|
|
||||||
@mixin panel {
|
|
||||||
@include shadow();
|
|
||||||
background-color: #1B1B1B;
|
|
||||||
border: 1px solid var(--color-border);
|
|
||||||
}
|
|
||||||
|
|
||||||
.window-wrapper {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
border: solid 1px var(--color-golden-border);
|
|
||||||
|
|
||||||
user-select: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
</style>
|
|
154
client/src/views/windows/game/dnd-5e/CreateItemPrompt.vue
Normal file
154
client/src/views/windows/game/dnd-5e/CreateItemPrompt.vue
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
<script setup>
|
||||||
|
import WindowHandle from '@/views/partials/WindowHandle.vue';
|
||||||
|
|
||||||
|
import { onMounted, ref } from 'vue';
|
||||||
|
import { SetupHandle, SetSize, ResetPosition } from '@/services/Windows';
|
||||||
|
import { ClearWindow, CreateWindow } from '../../../../services/Windows';
|
||||||
|
const props = defineProps(['data']);
|
||||||
|
const data = props.data;
|
||||||
|
|
||||||
|
const handle = ref(null);
|
||||||
|
let id = data.id;
|
||||||
|
|
||||||
|
const radioContainer = ref(null);
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
SetupHandle(id, handle);
|
||||||
|
SetSize(id, {width: 250, height: 320});
|
||||||
|
ResetPosition(id, "center");
|
||||||
|
});
|
||||||
|
|
||||||
|
function ConfirmSelection(){
|
||||||
|
let selected = radioContainer.value.querySelector('input[name="selector"]:checked');
|
||||||
|
if(!selected) return;
|
||||||
|
let value = selected.value;
|
||||||
|
|
||||||
|
CreateWindow('item_sheet', {
|
||||||
|
id: 'item_sheet',
|
||||||
|
title: 'Edit Item',
|
||||||
|
item_type: value,
|
||||||
|
close: () => ClearWindow('item_sheet')
|
||||||
|
});
|
||||||
|
|
||||||
|
ClearWindow(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="window-wrapper" :id="'window-wrapper-' + id">
|
||||||
|
<WindowHandle :window="id" ref="handle"></WindowHandle>
|
||||||
|
|
||||||
|
<div class="main-container">
|
||||||
|
<div class="radio-container" ref="radioContainer">
|
||||||
|
<div class="radio-item">
|
||||||
|
<img class="icon" src="icons/game-icons/000000/lorc/crossed-swords.svg">
|
||||||
|
<span>Weapon</span>
|
||||||
|
<input type="radio" name="selector" value="Weapon">
|
||||||
|
</div>
|
||||||
|
<div class="radio-item">
|
||||||
|
<img class="icon" src="icons/game-icons/000000/delapouite/shoulder-armor.svg">
|
||||||
|
<span>Equipment</span>
|
||||||
|
<input type="radio" name="selector" value="Equipment">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="radio-item">
|
||||||
|
<img class="icon" src="icons/game-icons/000000/lorc/potion-ball.svg">
|
||||||
|
<span>Consumable</span>
|
||||||
|
<input type="radio" name="selector" value="Consumable">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="radio-item">
|
||||||
|
<img class="icon" src="icons/game-icons/000000/delapouite/backpack.svg">
|
||||||
|
<span>Container</span>
|
||||||
|
<input type="radio" name="selector" value="Container">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="radio-item">
|
||||||
|
<img class="icon" src="icons/game-icons/000000/lorc/claw-hammer.svg">
|
||||||
|
<span>Tool</span>
|
||||||
|
<input type="radio" name="selector" value="Tool">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button class="btn-primary sound-click submit" v-on:click.prevent="ConfirmSelection">
|
||||||
|
<img class="text-icon invert" src="icons/iconoir/regular/check.svg">
|
||||||
|
Create new item
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
@mixin shadow {
|
||||||
|
-moz-box-shadow: 0px 0px 10px -1px rgba(0,0,0,0.25);
|
||||||
|
box-shadow: 0px 0px 10px -1px rgba(0,0,0,0.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin panel {
|
||||||
|
@include shadow();
|
||||||
|
background-color: #1B1B1B;
|
||||||
|
border: 1px solid var(--color-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.info {
|
||||||
|
text-align: left;
|
||||||
|
font-size: 16px;
|
||||||
|
width: 100%;
|
||||||
|
padding: 10px 10px 10px 17px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.window-wrapper {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.radio-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.radio-item {
|
||||||
|
padding: 10px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
span {
|
||||||
|
flex-grow: 1;
|
||||||
|
text-align: left;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="radio"]{
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
margin-right: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
border-bottom: 1px solid var(--color-border);
|
||||||
|
transition: color 1s;
|
||||||
|
|
||||||
|
&:first-child {
|
||||||
|
border-top: 1px solid var(--color-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
color: var(--color-text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.submit {
|
||||||
|
margin: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
190
client/src/views/windows/game/dnd-5e/ItemSheet.vue
Normal file
190
client/src/views/windows/game/dnd-5e/ItemSheet.vue
Normal file
@ -0,0 +1,190 @@
|
|||||||
|
<script setup>
|
||||||
|
import WindowHandle from '@/views/partials/WindowHandle.vue';
|
||||||
|
|
||||||
|
import { onMounted, ref } from 'vue';
|
||||||
|
import { SetupHandle, SetSize, ResetPosition } from '@/services/Windows';
|
||||||
|
import { CreateWindow } from '../../../../services/Windows';
|
||||||
|
import IconSelector from '../../../partials/IconSelector.vue';
|
||||||
|
import { AddContextMenu, HideContextMenu, ShowContextMenu } from '../../../../services/ContextMenu';
|
||||||
|
const props = defineProps(['data']);
|
||||||
|
const data = props.data;
|
||||||
|
|
||||||
|
const handle = ref(null);
|
||||||
|
const item_type = ref("");
|
||||||
|
|
||||||
|
const rarity = ref(null);
|
||||||
|
const weaponType = ref(null);
|
||||||
|
|
||||||
|
let id = data.id;
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
SetupHandle(id, handle);
|
||||||
|
SetSize(id, {width: 500, height: 400});
|
||||||
|
ResetPosition(id, "center");
|
||||||
|
|
||||||
|
item_type.value = data.item_type;
|
||||||
|
|
||||||
|
const rarities = [
|
||||||
|
{name: "⠀",
|
||||||
|
action: () => { rarity.value.innerHTML = ""; HideContextMenu(); }
|
||||||
|
},
|
||||||
|
{name: "<span class='common'>Common</span>",
|
||||||
|
action: () => { rarity.value.innerHTML = "<span class='important common'>Common</span>"; HideContextMenu(); }
|
||||||
|
},
|
||||||
|
{name: "<span class='uncommon'>Uncommon</span>",
|
||||||
|
action: () => { rarity.value.innerHTML = "<span class='important uncommon'>Uncommon</span>"; HideContextMenu(); }
|
||||||
|
},
|
||||||
|
{name: "<span class='rare'>Rare</span>",
|
||||||
|
action: () => { rarity.value.innerHTML = "<span class='important rare'>Rare</span>"; HideContextMenu(); }
|
||||||
|
},
|
||||||
|
{name: "<span class='very-rare'>Very rare</span>",
|
||||||
|
action: () => { rarity.value.innerHTML = "<span class='important very-rare'>Very rare</span>"; HideContextMenu(); }
|
||||||
|
},
|
||||||
|
{name: "<span class='legendary'>Legendary</span>",
|
||||||
|
action: () => { rarity.value.innerHTML = "<span class='important legendary'>Legendary</span>"; HideContextMenu(); }
|
||||||
|
},
|
||||||
|
{name: "<span class='artifact'>Artifact</span>",
|
||||||
|
action: () => { rarity.value.innerHTML = "<span class='important artifact'>Artifact</span>"; HideContextMenu(); }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
const weapon_types = [
|
||||||
|
{name: "⠀",
|
||||||
|
action: () => { weaponType.value.innerHTML = ""; HideContextMenu(); }
|
||||||
|
},
|
||||||
|
{name: "Melee",
|
||||||
|
action: () => { weaponType.value.innerHTML = "<span class='important'>Melee</span>"; HideContextMenu(); }
|
||||||
|
},
|
||||||
|
{name: "Ranged",
|
||||||
|
action: () => { weaponType.value.innerHTML = "<span class='important'>Ranged</span>"; HideContextMenu(); }
|
||||||
|
},
|
||||||
|
{name: "Martial Melee",
|
||||||
|
action: () => { weaponType.value.innerHTML = "<span class='important'>Martial Melee</span>"; HideContextMenu(); }
|
||||||
|
},
|
||||||
|
{name: "Martial Ranged",
|
||||||
|
action: () => { weaponType.value.innerHTML = "<span class='important'>Martial Ranged</span>"; HideContextMenu(); }
|
||||||
|
},
|
||||||
|
{name: "Natural",
|
||||||
|
action: () => { weaponType.value.innerHTML = "<span class='important'>Natural</span>"; HideContextMenu(); }
|
||||||
|
},
|
||||||
|
{name: "Improvised",
|
||||||
|
action: () => { weaponType.value.innerHTML = "<span class='important'>Improvised</span>"; HideContextMenu(); }
|
||||||
|
},
|
||||||
|
{name: "Siege Weapon",
|
||||||
|
action: () => { weaponType.value.innerHTML = "<span class='important'>Siege Weapon</span>"; HideContextMenu(); }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
rarity.value.addEventListener("click", () => {
|
||||||
|
ShowContextMenu(rarities)
|
||||||
|
});
|
||||||
|
AddContextMenu(rarity.value, rarities)
|
||||||
|
|
||||||
|
weaponType.value.addEventListener("click", () => {
|
||||||
|
ShowContextMenu(weapon_types)
|
||||||
|
});
|
||||||
|
AddContextMenu(weaponType.value, weapon_types)
|
||||||
|
|
||||||
|
weaponType.value.addEventListener("click", () => {
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="window-wrapper" :id="'window-wrapper-' + id">
|
||||||
|
<WindowHandle :window="id" ref="handle"></WindowHandle>
|
||||||
|
|
||||||
|
<div class="main-container">
|
||||||
|
<div class="item-header">
|
||||||
|
<IconSelector :window="id"></IconSelector>
|
||||||
|
<div class="header-info">
|
||||||
|
<h1 contenteditable="true" spellcheck="false">New Item</h1>
|
||||||
|
<div class="row">
|
||||||
|
<div class="grow subsection" ref="weaponType"></div>
|
||||||
|
<div class="grow subsection" ref="rarity"></div>
|
||||||
|
<!--
|
||||||
|
<option value="none"><span></span></option>
|
||||||
|
<option value="common"><span class="common">Common</span></option>
|
||||||
|
<option value="uncommon"><span class="uncommon">Uncommon</span></option>
|
||||||
|
<option value="rare"><span class="rare">Rare</span></option>
|
||||||
|
<option value="very rare"><span class="very-rare">Very Rare</span></option>
|
||||||
|
<option value="legendary"><span class="legendary">Legendary</span></option>
|
||||||
|
<option value="artifact"><span class="artifact">Artifact</span></option>-->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
@mixin shadow {
|
||||||
|
-moz-box-shadow: 0px 0px 10px -1px rgba(0,0,0,0.25);
|
||||||
|
box-shadow: 0px 0px 10px -1px rgba(0,0,0,0.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin panel {
|
||||||
|
@include shadow();
|
||||||
|
background-color: #1B1B1B;
|
||||||
|
border: 1px solid var(--color-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.item-header {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
height: 88px;
|
||||||
|
padding: 10px;
|
||||||
|
background-color: #1d1d1d;
|
||||||
|
|
||||||
|
.book-info {
|
||||||
|
margin-left: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-info {
|
||||||
|
display: flex;
|
||||||
|
flex-grow: 1;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
margin-left: 12px;
|
||||||
|
font-family: NodestoCapsCondensed;
|
||||||
|
font-weight: lighter;
|
||||||
|
font-size: 32px;
|
||||||
|
line-height: 32px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.row {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grow {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.window-wrapper {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.subsection {
|
||||||
|
height: 32px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
&:first-child {
|
||||||
|
border-right: 1px solid var(--color-border);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
118
client/src/views/windows/selectors/IconSelectorWindow.vue
Normal file
118
client/src/views/windows/selectors/IconSelectorWindow.vue
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
<script setup>
|
||||||
|
import WindowHandle from '@/views/partials/WindowHandle.vue';
|
||||||
|
|
||||||
|
import { onMounted, ref } from 'vue';
|
||||||
|
import { SetMinSize, SetMaxSize, SetResizable, SetupHandle, SetSize, ResetPosition } from '@/services/Windows';
|
||||||
|
import { CallWindow } from '../../../services/Windows';
|
||||||
|
import { GetIcons } from '../../../services/Resources';
|
||||||
|
const props = defineProps(['data']);
|
||||||
|
const data = props.data;
|
||||||
|
|
||||||
|
const handle = ref(null);
|
||||||
|
const imgContainer = ref(null);
|
||||||
|
|
||||||
|
let id = data.id;
|
||||||
|
|
||||||
|
let selectedImageElement;
|
||||||
|
let selectedFile;
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
SetupHandle(id, handle);
|
||||||
|
SetSize(id, {width: 310, height: 450});
|
||||||
|
SetResizable(id, true);
|
||||||
|
SetMinSize(id, {width: 310, height: 200});
|
||||||
|
ResetPosition(id, "center");
|
||||||
|
|
||||||
|
let images = GetIcons();
|
||||||
|
let imageObserver = new IntersectionObserver((entries, observer) => {
|
||||||
|
entries.forEach(function(entry) {
|
||||||
|
if (entry.isIntersecting) {
|
||||||
|
var image = entry.target;
|
||||||
|
image.src = image.dataset.src;
|
||||||
|
// image.classList.remove("lazy");
|
||||||
|
imageObserver.unobserve(image);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
images.forEach(image => {
|
||||||
|
let imgElement = document.createElement('img');
|
||||||
|
imgElement.setAttribute('data-src', image.path);
|
||||||
|
imgElement.setAttribute('draggable', 'false')
|
||||||
|
imgElement.classList.add('icon-element');
|
||||||
|
|
||||||
|
imgElement.addEventListener('click', (e) => {
|
||||||
|
if(selectedImageElement) selectedImageElement.classList.remove('selected');
|
||||||
|
selectedImageElement = imgElement;
|
||||||
|
selectedFile = image;
|
||||||
|
imgElement.classList.add('selected');
|
||||||
|
});
|
||||||
|
|
||||||
|
imageObserver.observe(imgElement);
|
||||||
|
imgContainer.value.appendChild(imgElement);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
function ConfirmSelection(){
|
||||||
|
CallWindow(id, 'done', {
|
||||||
|
selected: selectedFile
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="window-wrapper" :id="'window-wrapper-' + id">
|
||||||
|
<WindowHandle :window="id" ref="handle"></WindowHandle>
|
||||||
|
|
||||||
|
<div class="content">
|
||||||
|
<div class="images-container" ref="imgContainer">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="footer">
|
||||||
|
<button class="btn-primary sound-click" v-on:click.prevent="ConfirmSelection">Select</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.icon-element {
|
||||||
|
width: 50px;
|
||||||
|
height: 50px;
|
||||||
|
margin-bottom: -7px;
|
||||||
|
margin-left: 1px;
|
||||||
|
|
||||||
|
&.selected {
|
||||||
|
border: 1px solid rgb(43, 188, 255);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
|
||||||
|
.images-container {
|
||||||
|
flex-grow: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
.content {
|
||||||
|
width: 100%;
|
||||||
|
height: calc(100% - 24px);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.window-wrapper {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
</style>
|
12
package.json
12
package.json
@ -1 +1,11 @@
|
|||||||
{}
|
{
|
||||||
|
"name": "dragonroll",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "Vtt",
|
||||||
|
"main": "prebuild.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
},
|
||||||
|
"author": "Aran Roig",
|
||||||
|
"license": "ISC"
|
||||||
|
}
|
||||||
|
40
prebuild.js
Executable file
40
prebuild.js
Executable file
@ -0,0 +1,40 @@
|
|||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
const folderPaths = [
|
||||||
|
'./client/public/icons/weapons',
|
||||||
|
'./client/public/icons/equipment',
|
||||||
|
'./client/public/icons/consumables',
|
||||||
|
'./client/public/icons/magic',
|
||||||
|
'./client/public/icons/skills',
|
||||||
|
'./client/public/icons/sundries',
|
||||||
|
'./client/public/icons/tools',
|
||||||
|
'./client/public/icons/commodities',
|
||||||
|
'./client/public/icons/containers',
|
||||||
|
'./client/public/icons/creatures',
|
||||||
|
'./client/public/icons/environment',
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
const outputPath = 'client/public/data/icons.json'
|
||||||
|
let result = [];
|
||||||
|
|
||||||
|
let iconCount = 0;
|
||||||
|
for(let i = 0; i < folderPaths.length; i++){
|
||||||
|
const files = fs.readdirSync(folderPaths[i], { recursive: true });
|
||||||
|
for(let j = 0; j < files.length; j++){
|
||||||
|
let absPath = folderPaths[i] + "/" + files[j]
|
||||||
|
if(fs.lstatSync(absPath).isFile()){
|
||||||
|
result.push({
|
||||||
|
path: absPath.replace(/^(\.\/client\/public\/)/,""),
|
||||||
|
name: path.parse(absPath).name
|
||||||
|
});
|
||||||
|
iconCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
fs.writeFileSync(outputPath, JSON.stringify({files: result}, null, 2));
|
||||||
|
|
||||||
|
console.log('File list generated successfully!');
|
||||||
|
console.log("Generated " + iconCount + " icons");
|
Loading…
Reference in New Issue
Block a user