This commit is contained in:
Nordi98 2025-08-11 16:51:34 +02:00
parent 600d79af31
commit 5d11084641
136 changed files with 12007 additions and 584 deletions

View file

@ -0,0 +1,229 @@
<script lang="ts">
import { onMount } from "svelte";
import Icon from "./Icon.svelte";
import { isDevMode, showComponent, showUi } from "../stores/GeneralStores";
import { drawTextStore, hideDrawTextMenu, hideDrawTextStore } from "../stores/DrawTextStore";

let drawTextDataValue:any = {};
drawTextStore.subscribe((value) => drawTextDataValue = value);

let hideDrawTextValue = false;
hideDrawTextStore.subscribe(value => {
hideDrawTextValue = value;
});

let marginLeftVal = '2%';
let interactionText = [], colorValue = '';
onMount(() => {
getColorValue();
handleInteractionText();

if(isDevMode) {
setTimeout(() => {
hideDrawTextMenu();
}, 10000);
}
});

function getColorValue() {
switch(drawTextDataValue.color) {
case "primary":
colorValue ="#0275d8";
break;
case "error":
colorValue = "#d9534f";
break;
case "success":
colorValue = "#5cb85c";
break;
case "warning":
colorValue = "#f0ad4e";
break;
case "info":
colorValue = "#5bc0de";
break;
case "mint":
colorValue = "#a1f8c7";
break;
default:
colorValue = "var(--color-green)";
break;
}
}

function handleInteractionText() {
let matches = drawTextDataValue.keys.match(/\[(.*?)\]/);
if (matches) {
let submatch = matches[0];

let splitText = drawTextDataValue.keys.split(submatch);
interactionText = [
{
value: splitText[0],
color: 'var(--color-lightgrey)'
},
{
value: submatch,
color: colorValue
},
{
value: splitText[1],
color: 'var(--color-lightgrey)'
}
];
} else {
interactionText = [
{
value: drawTextDataValue.keys,
color: 'var(--color-lightgrey)'
}
];
}
}

function closeDrawText() {
const statusWrapperDom = document.getElementById('draw-text-wrapper');
if(statusWrapperDom) {
statusWrapperDom.style.animation = '2s slide-left';

let keyFrames = document.createElement('style');
keyFrames.innerHTML = `
@keyframes slide-left {
from {
margin-left: `+marginLeftVal+`;
}
to {
margin-left: -20%;
}
}

.draw-text-wrapper {
-moz-animation: 2s slide-left;
-webkit-animation: 2s slide-left;
animation: 2s slide-left;
}
`;

statusWrapperDom.appendChild(keyFrames);

setTimeout(() => {
showUi.set(false);
showComponent.set(null);
drawTextStore.set({
// title: '',
icon: '',
keys: '',
color: ''
});
hideDrawTextStore.set(false);
}, 500);
}
}

$: {
if(hideDrawTextValue) {
closeDrawText();
}

if(drawTextDataValue) {
handleInteractionText();
}
}
</script>

<div id="draw-text-wrapper" class="draw-text-wrapper" style="margin-left:{marginLeftVal};">
<div class="draw-text-title-wrapper">
<div class="icon">
<Icon icon={drawTextDataValue.icon} styleColor={colorValue} />
</div>

<div class="title-info">
<p class="title-description">
{#each interactionText as text}
<p style="color: {text.color};">{text.value}</p>
{/each}
</p>
</div>
</div>
</div>

<style>
.draw-text-wrapper {
-moz-animation: 1s slide-right;
-webkit-animation: 1s slide-right;
animation: 1s slide-right;
position: absolute;
top: 50%;
transform: translateY(-50%);

min-width: 10vw;
width: max-content;

min-height: 3vw;
height: fit-content;

overflow: hidden;

background-color: var(--color-darkblue);
border-radius: 0.3vw;

padding: 0.75vw 1.5vw;
display: flex;
flex-direction: column;
}
@keyframes slide-right {
from {
margin-left: -20%;
}
to {
margin-left: 2%;
}
}
@keyframes slide-left {
from {
margin-left: 2%;
}
to {
margin-left: -100%;
}
}

.draw-text-wrapper > .draw-text-title-wrapper {
display: flex;
flex-direction: row;
}

.draw-text-wrapper > .draw-text-title-wrapper > .icon {
width: 1.5vw;
margin: auto 0.75vw auto 0;

font-size: 1.25vw;
}
.draw-text-wrapper > .draw-text-title-wrapper > .title-info {
display: flex;
flex-direction: column;
text-transform: capitalize;
}

.draw-text-wrapper > .draw-text-title-wrapper > .title-info > .title-description {
font-size: 1vw;
font-weight: 300;
color: var(--color-lightgrey);

display: flex;
flex-direction: row;
word-wrap: break-word;
flex-wrap: wrap;

margin-top: auto;
margin-bottom: auto;
}
.draw-text-wrapper > .draw-text-title-wrapper > .title-info > .title-description > p {
margin-right: 0.25vw;
}
</style>

View file

@ -0,0 +1,8 @@
<script lang="ts">
export let icon: string;
export let color: string = '';
export let classes: string = '';
export let styleColor: string = '';
</script>

<i class="{color} {icon} {classes}" style="color: {styleColor};" />

View file

@ -0,0 +1,13 @@
<script lang="ts">
import { imageStore } from './../stores/ImageStore';
import { fly } from 'svelte/transition';
</script>

{#if $imageStore.show}
<div class="flex items-center justify-center min-h-screen" transition:fly="{{y: +400}}">
<div class="flex items-center flex-col ps-bg-darkblue p-10 shadow-md shadow-gray-800 rounded-md">
<!-- svelte-ignore a11y-img-redundant-alt -->
<img src={$imageStore.url} alt="Image Placeholder" />
</div>
</div>
{/if}

View file

@ -0,0 +1,203 @@
<script lang="ts">
import { hideUi, isDevMode } from "../stores/GeneralStores";
import { inputStore } from "../stores/InputStores";
import Icon from "./Icon.svelte";
import { onMount } from "svelte";
import fetchNui from "../../utils/fetch";
import { handleKeyUp } from "../../utils/eventHandler";

document.onkeyup = handleKeyUp;
let inputData:any = $inputStore;

onMount(() => {
inputData = inputData.map((val) => {
val.value = null;
return val;
});
});

function submit() {
let returnData = [];

let inputs = document.querySelectorAll('input');
inputs.forEach((input: HTMLInputElement, index) => {
let returnObj = {
id: input.id,
value: input.value,
// compIndex: index
};
returnData.push(returnObj)
});

if(!isDevMode) {
fetchNui('input-callback', returnData);
}

closeInputs();
}

export function closeInputs(): void {
let inputs = document.querySelectorAll('input');
inputs.forEach((input: HTMLInputElement) => {
input.value = '';
});

inputData = [];

if(!isDevMode) {
fetchNui('input-close', { ok: true });
}
hideUi();
}
</script>

<div class="input-base-wrapper">
<div class="logo">
<img src="./images/ps-logo.png" alt="ps-logo" />
</div>

<div class="input-form">
{#each inputData as inputValue}
<div class="input-wrapper">
<div class="input-data-wrapper">
<div class="input-icon">
<Icon icon={inputValue.icon} color="ps-text-green" classes="text-2xl" />
</div>
<div class="input-area">
<p class="label">
{inputValue.label}
</p>
<input id={inputValue.id} type={inputValue.type} class="value" placeholder={inputValue.placeholder} value={inputValue.value} />
</div>
</div>
<div class="horizontal-line"></div>
<!-- <div class="input-message">
{inputValue.placeholder}
</div> -->
</div>
{/each}
</div>

<div class="button-wrapper">
<button class="submit-btn" on:click={submit}>Submit</button>
<button class="cancel-btn" on:click={closeInputs}>Cancel</button>
</div>
</div>

<style>
.input-base-wrapper {
/* centering the view */
position: absolute;
left: 50%;
top: 50%;
-webkit-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);

width: 25vw;
min-height: 28vw;
height: fit-content;

overflow: hidden;

background-color: var(--color-darkblue);
border-radius: 0.3vw;

padding: 0.5vw 2vw;
display: flex;
flex-direction: column;
}

.input-base-wrapper > .input-form {
margin-top: 1vw;
height: 100%;

/* border: 0.1px solid red; */
}

.input-base-wrapper > .input-form > .input-wrapper {
display: flex;
flex-direction: column;
}

.input-base-wrapper > .input-form > .input-wrapper > .horizontal-line {
width: inherit;
height: 1px;
background-color: var(--color-lightgrey);
}
.input-base-wrapper > .input-form > .input-wrapper > .input-message {
font-size: 0.8vw;
opacity: 0.85;
color: var(--color-lightgrey);
margin-top: 0.2vw;
text-transform: capitalize;
}
.input-base-wrapper > .input-form > .input-wrapper > .input-data-wrapper {
display: flex;
flex-direction: row;

padding: 0.5vw 0;
}
.input-base-wrapper > .input-form > .input-wrapper:not(:last-child) {
margin-bottom: 1vw;
}

.input-base-wrapper > .input-form > .input-wrapper > .input-data-wrapper > .input-icon {
margin-right: 0.75vw;
width: 1.5vw;

margin-top: auto;
margin-bottom: auto;
}
.input-base-wrapper > .input-form > .input-wrapper > .input-data-wrapper > .input-area {
display: flex;
flex-direction: column;

color: var(--color-lightgrey);
}
.input-base-wrapper > .input-form > .input-wrapper > .input-data-wrapper > .input-area > .label {
font-size: 1vw;
font-weight: 500 !important;
}
.input-base-wrapper > .input-form > .input-wrapper > .input-data-wrapper > .input-area > .value {
font-size: 0.8vw;
font-weight: 300 !important;
width: 18vw;
background-color: inherit;
margin-top: 0.2vw;
}

input:focus {
padding-left: 0;
outline: none;
border-bottom-width: 2px;
border-bottom-color: var(--color-green);
margin-bottom: -1px;
}

.input-base-wrapper > .button-wrapper {
display: flex;
flex-direction: row;
justify-content: space-evenly;

margin: 1.5vw auto 1.25vw auto;
color: var(--color-black);
}
.input-base-wrapper > .button-wrapper > .submit-btn {
background-color: var(--color-green);
}
.input-base-wrapper > .button-wrapper > .cancel-btn {
background-color: var(--color-darkgrey);
}

button {
border-radius: 0.3vw;
padding: 0.3vw 1vw;
text-transform: uppercase;
font-weight: 500 !important;
}
button:not(:last-child) {
margin-right: 0.5vw;
}
</style>

View file

@ -0,0 +1,256 @@
<script lang="ts">
import { closeInteractionMenu, menuStore } from "../stores/MenuStores";
import Icon from "./Icon.svelte";
import fetchNui from "../../utils/fetch";
import { isDevMode } from "../stores/GeneralStores";

let menuData:Array<any> = $menuStore;
let selectedMenuItem = null;
let subMenu = null;

let subMenuTextColorOverride = {
id: null, color: 'black'
};
let menuTextColorOverride = {
id: null, color: 'black'
};

function handleMenuSelection(selectedMenu) {
selectedMenuItem = selectedMenu;

if(selectedMenu) {
if(!selectedMenu.subMenu && !isDevMode) {
fetchNui('MenuSelect', {data :selectedMenu});
closeInteractionMenu();
} else {
subMenu = selectedMenuItem.subMenu;
}
}
}

function handleSubMenuSelection(selectedSubMenu) {
const formData = {
data:selectedSubMenu
};
if(!isDevMode) {
fetchNui('MenuSelect', formData);
closeInteractionMenu();
}
}

function handleItemHover(itemId, index, color, action='enter', isSubMenu = false) {
const itemDom = document.getElementById(itemId);
if(action === 'enter') {
if(isSubMenu) {
itemDom.style.backgroundColor = color || 'var(--color-green)';
subMenuTextColorOverride.id = index;
} else {
itemDom.style.backgroundColor = color || 'var(--color-green)';
menuTextColorOverride.id = index;
}
} else {
itemDom.style.backgroundColor = 'var(--color-darkblue)';
subMenuTextColorOverride.id = null;
menuTextColorOverride.id = null;
}
}
</script>

<div class="menu-base-wrapper">
<div class="screen-base">
{#if !selectedMenuItem}
<div class="header-slot" style="border: 3px solid var(--color-green);">
<img src="./images/ps-logo.png" alt="ps-logo" />
</div>

<div class="screen-body">
{#each menuData as menu, index}
<div id={"menu-"+index} class="each-panel"
on:mouseenter={() => handleItemHover("menu-"+index, index, menu.color, 'enter', false)}
on:mouseleave={() => handleItemHover("menu-"+index, index, menu.color, 'leave', false)}
on:click={() => handleMenuSelection(menu)}>
<div class="menu-icon">
<Icon icon={menu.icon} styleColor={menuTextColorOverride.id === index ? menuTextColorOverride.color : menu?.color || 'var(--color-green)'} />
</div>
<div class="menu-details">
<p class="header" style="color: {menuTextColorOverride.id === index ? menuTextColorOverride.color : 'var(--color-white)'};">{menu.header}</p>
{#if menu.hasOwnProperty('text')}
<p class="text" style="color: {menuTextColorOverride.id === index ? menuTextColorOverride.color : 'var(--color-lightgrey)'};">{menu.text}</p>
{/if}
</div>
{#if menu?.subMenu}
<div class="chevron" style="color: {menuTextColorOverride.id === index ? menuTextColorOverride.color : 'var(--color-white)'};">
<i class="fa-solid fa-chevron-right"></i>
</div>
{/if}
</div>
{/each}
</div>
{:else if selectedMenuItem.hasOwnProperty('subMenu') && selectedMenuItem.subMenu}
<div class="submenu-header-slot" on:click={() => handleMenuSelection(null)}>
<i class="fa-solid fa-chevron-left left-chevron"></i>
<p class="main-menu">Main Menu</p>
</div>

<div class="screen-body">
{#each subMenu as menu, index}
<div id={"sub-menu-"+index} class="each-panel"
on:mouseenter={() => handleItemHover("sub-menu-"+index, index, menu.color, 'enter', true)}
on:mouseleave={() => handleItemHover("sub-menu-"+index, index, menu.color, 'leave', true)}
on:click={() => handleSubMenuSelection(menu)}>
<div id={"menu-icon-"+index} class="menu-icon">
<Icon icon={menu.icon} styleColor={subMenuTextColorOverride.id === index ? subMenuTextColorOverride.color : menu.color || 'var(--color-green)'} />
</div>
<div class="menu-details">
<p class="header" style="color: {subMenuTextColorOverride.id === index ? subMenuTextColorOverride.color : 'var(--color-white)'};">{menu.header}</p>
{#if menu.hasOwnProperty('text')}
<p class="text" style="color: {subMenuTextColorOverride.id === index ? subMenuTextColorOverride.color : 'var(--color-lightgrey)'};">{menu.text}</p>
{/if}
</div>
</div>
{/each}
</div>
{/if}
</div>
</div>

<style>
.menu-base-wrapper {
/* centering the view */
position: absolute;
left: 70%;
top: 40%;
-webkit-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);

width: 20vw;
height: 30vw;
/* min-height: 4vw;
height: fit-content; */

overflow: hidden;
/* border: 0.1px solid red; */
}

.menu-base-wrapper > .screen-base {
height: 100%;
/* border: 0.1px solid yellow; */

display: flex;
flex-direction: column;
}

.menu-base-wrapper > .screen-base > .header-slot {
background-color: var(--color-darkblue);
border-radius: 0.3vw;

padding: 0.5vw 2vw;
display: flex;
flex-direction: row;

min-height: 3vw;
height: fit-content;
}
.menu-base-wrapper > .screen-base > .header-slot > img {
height: 4vw;
align-self: center;
}

.menu-base-wrapper > .screen-base > .submenu-header-slot {
min-height: 4vw;
height: max-content;

background-color: var(--color-darkblue);
border-radius: 0.3vw;
color: var(--color-white);

padding: 0.3vw 1vw;
cursor: pointer;

display: flex;
flex-direction: row;
}
.menu-base-wrapper > .screen-base > .submenu-header-slot > .left-chevron {
margin-top: auto;
margin-bottom: auto;
margin-right: 0.5vw;

width: 1.5vw;
font-size: 1.1vw;
}
.menu-base-wrapper > .screen-base > .submenu-header-slot > .main-menu {
font-weight: 500;
font-size: 1.25vw;

margin-top: auto;
margin-bottom: auto;
}

.menu-base-wrapper > .screen-base > .screen-body {
margin-top: 0.3vw;
height: 100%;
overflow-y: auto;
display: flex;
flex-direction: column;
/* border: 0.1px solid blue; */
}

.menu-base-wrapper > .screen-base > .screen-body > .each-panel {
min-height: 4vw;
height: max-content;

background-color: var(--color-darkblue);
border-radius: 0.3vw;

padding: 0.5vw 1vw;
cursor: pointer;

display: flex;
flex-direction: row;
}
.menu-base-wrapper > .screen-base > .screen-body > .each-panel:not(:last-child) {
margin-bottom: 0.3vw;
}

.menu-base-wrapper > .screen-base > .screen-body > .each-panel > .menu-icon {
margin-top: auto;
margin-bottom: auto;
margin-right: 0.5vw;

width: 1.5vw;
font-size: 1vw;
}

.menu-base-wrapper > .screen-base > .screen-body > .each-panel > .menu-details {
display: flex;
flex-direction: column;
margin-top: auto;
margin-bottom: auto;
}

.menu-base-wrapper > .screen-base > .screen-body > .each-panel > .menu-details > .header {
font-size: 0.8vw;
white-space: nowrap;
/* color: var(--color-white); */
}

.menu-base-wrapper > .screen-base > .screen-body > .each-panel > .menu-details > .text {
font-size: 0.6vw;
white-space: nowrap;
/* color: var(--color-lightgrey); */
}

.menu-base-wrapper > .screen-base > .screen-body > .each-panel > .chevron {
margin-left: 72%;
font-size: 1vw;
/* color: var(--color-white); */

margin-top: auto;
margin-bottom: auto;
}
</style>

View file

@ -0,0 +1,27 @@
<script lang="ts">
import { notifications } from './../stores/NotificationStore';
import { NotificationIcons, NotificationTypes } from './../enums/NotificationTypesEnum';
</script>

<div class="flex justify-end min-h-screen">
<div class="flex flex-col mr-4 mt-4 w-[200px]">
{#each $notifications as notification}
<div
class="{notification.type} flex items-center px-4 py-3 mb-1 text-white min-w-full rounded"
>
<div class="text-2xl mt-1">
{#if notification.type === NotificationTypes.Success}
<i class={NotificationIcons.Success} />
{:else if notification.type === NotificationTypes.Warning}
<i class={NotificationIcons.Warning} />
{:else if notification.type === NotificationTypes.Error}
<i class={NotificationIcons.Error} />
{:else if notification.type === NotificationTypes.Info}
<i class={NotificationIcons.Info} />
{/if}
</div>
<span class="ml-3">{notification.text}</span>
</div>
{/each}
</div>
</div>

View file

@ -0,0 +1,195 @@
<script lang="ts">
import { hideStatusBar, hideStatusBarStore, statusBarStore } from "../stores/StatusBarStores";
import { onMount } from "svelte";
import Icon from "./Icon.svelte";
import type { IStatusBarItem } from "src/interfaces/IStatusBar";
import { isDevMode, showComponent, showUi } from "../stores/GeneralStores";

let statusData:any = $statusBarStore;
let statusDataItems:[IStatusBarItem] = statusData.items;

statusBarStore.subscribe((value) => {
statusData = value;
statusDataItems = statusData.items;
});

let hideStatusBarValue = false;
hideStatusBarStore.subscribe(value => {
hideStatusBarValue = value;
});

onMount(() => {
if(isDevMode) {
setTimeout(() => {
hideStatusBar();
}, 10000);
}
});

function closeStatusBar() {
const statusWrapperDom = document.getElementById('status-bar-wrapper');
if(statusWrapperDom) {
statusWrapperDom.style.animation = '2s hide-statusbar';

let keyFrames = document.createElement('style');
keyFrames.innerHTML = `
@keyframes hide-statusbar {
from {
opacity: 1;
}
to {
opacity: 0;
}
}

.status-bar-wrapper {
-moz-animation: 2s hide-statusbar;
-webkit-animation: 2s hide-statusbar;
animation: 2s hide-statusbar;
}
`;

statusWrapperDom.appendChild(keyFrames);

setTimeout(() => {
showUi.set(false);
showComponent.set(null);
statusBarStore.set({
title: '',
description: '',
items: [],
icon: '',
});
hideStatusBarStore.set(false);
}, 500);
}
}

$: {
if(hideStatusBarValue) {
// if(hideStatusBarValue || !hideStatusBarValue)
closeStatusBar();
}
}
</script>

<div id="status-bar-wrapper" class="status-bar-wrapper">
<div class="status-title-wrapper">
<div class="icon">
<Icon icon={statusData.icon} color="ps-text-green" />
</div>

<div class="title-info">
<p class="title">
{statusData.title}
</p>
<p class="title-description">
{statusData.description}
</p>
</div>
</div>

<div class="items-wrapper">
{#each statusDataItems as item}
<div class="each-item">
<p class="label">
{item.key}:
</p>
<p class="value">
{item.value}
</p>
</div>
{/each}
</div>
</div>

<style>
.status-bar-wrapper {
-moz-animation: 2s display-status;
-webkit-animation: 2s display-status;
animation: 2s display-status;

position: absolute;
left: 50%;
bottom: 1%;
transform: translateX(-50%);

width: 23vw;
min-height: 8vw;
height: fit-content;

overflow: hidden;

background-color: var(--color-darkblue);
border-radius: 0.3vw;

padding: 1vw 2vw;
display: flex;
flex-direction: column;
}
@keyframes display-status {
from {
opacity: 0;
}
to {
opacity: 1;
}
}

.status-bar-wrapper > .status-title-wrapper {
display: flex;
flex-direction: row;
}

.status-bar-wrapper > .status-title-wrapper > .icon {
width: 1.5vw;
margin-right: 0.5vw;

font-size: 1.25vw;
}
.status-bar-wrapper > .status-title-wrapper > .title-info {
display: flex;
flex-direction: column;
text-transform: capitalize;
}
.status-bar-wrapper > .status-title-wrapper > .title-info > .title {
font-size: 1.3vw;
font-weight: 500;
color: var(--color-white);
}
.status-bar-wrapper > .status-title-wrapper > .title-info > .title-description {
font-size: 0.95vw;
font-weight: 200;
color: var(--color-lightgrey);

margin-top: -0.2vw;
}

.status-bar-wrapper > .items-wrapper {
margin-left: 2vw;
margin-top: 0.5vw;
}
.status-bar-wrapper > .items-wrapper > .each-item {
display: flex;
flex-direction: row;

word-wrap: break-word;
flex-wrap: wrap;

font-size: 0.95vw;
}
.status-bar-wrapper > .items-wrapper > .each-item:not(:last-child) {
margin-bottom: 0.3vw;
}

.status-bar-wrapper > .items-wrapper > .each-item > .label {
color: var(--color-lightgrey);
}
.status-bar-wrapper > .items-wrapper > .each-item > .value {
color: var(--color-green);
margin-left: 0.3vw;
}
</style>