fix
24
resources/[standalone]/ps-multijob/svelte-source/.gitignore
vendored
Normal file
|
@ -0,0 +1,24 @@
|
|||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
3
resources/[standalone]/ps-multijob/svelte-source/.vscode/extensions.json
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"recommendations": ["svelte.svelte-vscode"]
|
||||
}
|
34
resources/[standalone]/ps-multijob/svelte-source/global.css
Normal file
|
@ -0,0 +1,34 @@
|
|||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
font-family: 'Roboto', sans-serif;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
:root {
|
||||
--color-green: #02f1b5;
|
||||
--color-orange: #ff4545;
|
||||
--color-darkestblue: #131121;
|
||||
--color-darkerblue: #222033;
|
||||
--color-darkblue: #424057;
|
||||
--color-white: #ffffff;
|
||||
--color-black: #000000;
|
||||
--color-lightestgrey: #dadada;
|
||||
--color-lightgrey: #cacaca;
|
||||
--color-grey: #797979;
|
||||
--font-color: rgba(var(--theme-white), 0.87);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 5px;
|
||||
}
|
||||
::-webkit-scrollbar-thumb {
|
||||
border-radius: 2px;
|
||||
background-color: rgba(60, 60, 60, 1);
|
||||
}
|
26
resources/[standalone]/ps-multijob/svelte-source/index.html
Normal file
|
@ -0,0 +1,26 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>PS-MultiJob</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;500&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.0/css/all.min.css"
|
||||
integrity="sha512-xh6O/CkQoPOWDdYTDqeRdPCVd1SpvCA9XXcUnZS2FmJNp1coAFzvtCN9BmamE+4aHK8yyUHUSCcJHgXloTyT2A=="
|
||||
crossorigin="anonymous"
|
||||
referrerpolicy="no-referrer"
|
||||
/>
|
||||
<link rel="stylesheet" href="./global.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,34 @@
|
|||
{
|
||||
"name": "svelte-source",
|
||||
"private": true,
|
||||
"version": "1.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite --host",
|
||||
"build": "pnpm check && vite build",
|
||||
"preview": "vite preview --host",
|
||||
"check": "svelte-check --tsconfig ./tsconfig.json",
|
||||
"test": "vitest",
|
||||
"test:ui": "vitest --ui",
|
||||
"coverage": "vitest run --coverage"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@sveltejs/vite-plugin-svelte": "^1.0.1",
|
||||
"@testing-library/svelte": "^3.1.3",
|
||||
"@tsconfig/svelte": "^2.0.1",
|
||||
"@unocss/preset-uno": "^0.44.5",
|
||||
"@unocss/reset": "^0.44.5",
|
||||
"html-minifier": "^4.0.0",
|
||||
"jsdom": "^20.0.0",
|
||||
"sass": "^1.54.9",
|
||||
"svelte": "^3.49.0",
|
||||
"svelte-check": "^2.8.0",
|
||||
"svelte-preprocess": "^4.10.7",
|
||||
"tslib": "^2.4.0",
|
||||
"typescript": "^4.7.4",
|
||||
"unocss": "^0.44.5",
|
||||
"vite": ">=3.2.7",
|
||||
"vite-plugin-windicss": "^1.8.7",
|
||||
"vitest": "^0.18.1"
|
||||
}
|
||||
}
|
2180
resources/[standalone]/ps-multijob/svelte-source/pnpm-lock.yaml
generated
Normal file
|
@ -0,0 +1,44 @@
|
|||
<script lang="ts">
|
||||
import { fly } from 'svelte/transition';
|
||||
import CategoryMenu from './components/CategoryMenu.svelte';
|
||||
import NavBar from './components/NavBar.svelte';
|
||||
import { EventHandler } from './utils/eventHandler';
|
||||
import DebugMode from './stores/debugStore';
|
||||
import PanelStore from './stores/PanelStore';
|
||||
import JobStore from './stores/JobStore';
|
||||
import { mockJobMenuOpen } from './utils/mockEvent';
|
||||
|
||||
const { panelActive, show, side } = PanelStore;
|
||||
const { jobManifest } = JobStore;
|
||||
|
||||
EventHandler();
|
||||
document.onkeyup = PanelStore.handleKeyUp;
|
||||
|
||||
if (DebugMode) {
|
||||
mockJobMenuOpen();
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
{#if $show}
|
||||
<main class={"min-h-screen flex"+($side == "right" ? " justify-end ":" ")+(DebugMode ? "bg-dark-200": "bg-transparent")}>
|
||||
{#if $side == "right"}
|
||||
{#if $panelActive != ""}
|
||||
<div in:fly|local="{{x: 500, duration: 500}}" out:fly|local="{{x: 500, duration: 500}}">
|
||||
<CategoryMenu jobArray={$jobManifest[$panelActive] || []} panelName={$panelActive}/>
|
||||
</div>
|
||||
{/if}
|
||||
<NavBar side={$side}/>
|
||||
{:else}
|
||||
<NavBar side={$side}/>
|
||||
{#if $panelActive != ""}
|
||||
<div in:fly|local="{{x: -500, duration: 500}}" out:fly|local="{{x: -500, duration: 500}}">
|
||||
<CategoryMenu jobArray={$jobManifest[$panelActive] || []} panelName={$panelActive}/>
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
</main>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
</style>
|
|
@ -0,0 +1,41 @@
|
|||
<script lang="ts">
|
||||
import JobCard from './JobCard.svelte';
|
||||
import type { Job } from '../types/types';
|
||||
|
||||
export let jobArray: Array<Job> = [];
|
||||
export let panelName: string = "";
|
||||
|
||||
</script>
|
||||
|
||||
<main class="w-[380px] min-h-screen block pt-[20px] select-none">
|
||||
<div class="text-white px-[28px] pb-4">
|
||||
<p class="category">CATEGORY</p>
|
||||
<p class="category-name text-white block mt-[-5px] font-medium capitalize">
|
||||
{panelName} Jobs
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="max-h-screen overflow-y-auto px-[28px] pb-20">
|
||||
{#each jobArray as job (job.name)}
|
||||
<JobCard name={job.label} nuiName={job.name} nuiRank={job.grade} icon={job.icon} description={job.description}
|
||||
salary={job.salary} rank={job.gradeLabel} active={job.active} category={panelName}/>
|
||||
{/each}
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<style lang="scss">
|
||||
main {
|
||||
background: var(--color-darkestblue);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.category {
|
||||
font-size: 10pt;
|
||||
color: var(--color-lightestgrey);
|
||||
}
|
||||
|
||||
.category-name {
|
||||
font-size: 15pt;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,163 @@
|
|||
<script lang="ts">
|
||||
import JobDetail from './atoms/JobDetail.svelte';
|
||||
import SalarySVG from './atoms/svgs/SalarySVG.svelte';
|
||||
import RankSVG from './atoms/svgs/RankSVG.svelte';
|
||||
import ActiveSVG from './atoms/svgs/ActiveSVG.svelte';
|
||||
import SelectSVG from './atoms/svgs/SelectSVG.svelte';
|
||||
import CrossMarkSVG from './atoms/svgs/CrossMarkSVG.svelte';
|
||||
import DeleteSVG from './atoms/svgs/DeleteSVG.svelte';
|
||||
import ClockSVG from './atoms/svgs/ClockSVG.svelte';
|
||||
import TaxiSVG from './atoms/svgs/TaxiSVG.svelte';
|
||||
import JobStore from '../stores/JobStore';
|
||||
|
||||
export let name: string;
|
||||
export let nuiName: string;
|
||||
export let icon: string = "";
|
||||
export let description: string = "";
|
||||
export let salary: number;
|
||||
export let rank: string;
|
||||
export let nuiRank: number;
|
||||
export let active: number;
|
||||
export let category: string;
|
||||
|
||||
function getDutyText(onDuty: boolean) {
|
||||
return onDuty ? "On Duty" : "Off Duty";
|
||||
}
|
||||
|
||||
function getSelectText(select: boolean) {
|
||||
return select ? "Selected" : "Unselect";
|
||||
}
|
||||
|
||||
const { activeJob, onDuty, setActiveJob, toggleDuty, unSetActiveJob, deleteJob } = JobStore;
|
||||
|
||||
let isActive: boolean = false;
|
||||
$: isActive = $activeJob == nuiName;
|
||||
$: dutyText = getDutyText($onDuty);
|
||||
|
||||
let onDutyHover: boolean = false;
|
||||
let transitionOnDuty: boolean = false;
|
||||
let transitionOffDuty: boolean = false;
|
||||
|
||||
function handleOnDutyMouseEnter() {
|
||||
dutyText = getDutyText(!$onDuty);
|
||||
onDutyHover = true;
|
||||
}
|
||||
|
||||
function handleOnDutyMouseLeave() {
|
||||
dutyText = getDutyText($onDuty);
|
||||
onDutyHover = false;
|
||||
transitionOnDuty = false;
|
||||
transitionOffDuty = false;
|
||||
}
|
||||
|
||||
function handleDutyChange() {
|
||||
if ($onDuty) {
|
||||
transitionOffDuty = true;
|
||||
transitionOnDuty = false;
|
||||
} else {
|
||||
transitionOnDuty = true;
|
||||
transitionOffDuty = false;
|
||||
}
|
||||
toggleDuty();
|
||||
}
|
||||
|
||||
let selectText: string = "selected";
|
||||
let selectHover: boolean = false;
|
||||
|
||||
function handleOnSelectMouseEnter() {
|
||||
selectText = getSelectText(false);
|
||||
selectHover = true;
|
||||
}
|
||||
|
||||
function handleOnSelectMouseLeave() {
|
||||
selectText = getSelectText(true);
|
||||
selectHover = false;
|
||||
}
|
||||
|
||||
function handleUnSelectJob() {
|
||||
unSetActiveJob();
|
||||
selectHover = false;
|
||||
selectText = "selected";
|
||||
}
|
||||
</script>
|
||||
|
||||
<main class="job w-full flex flex-col gap-4 mb-[30px] b-rd-[10px] px-[22px] py-5
|
||||
relative select-none bg-[var(--color-darkerblue)] border border-[var(--color-darkblue)]">
|
||||
<div class="flex flex-row items-center gap-2 text-center">
|
||||
<div class="w-6 text-[var(--color-green)]">
|
||||
{#if icon}
|
||||
<i
|
||||
class="{icon} fa-lg"
|
||||
/>
|
||||
{:else}
|
||||
<svelte:component this={TaxiSVG} />
|
||||
{/if}
|
||||
</div>
|
||||
<p class="text-xl tracking-wide capitalize">
|
||||
{name}
|
||||
</p>
|
||||
<div class="w-7 text-[var(--color-darkblue)] cursor-pointer ml-auto hover:text-[var(--color-orange)]"
|
||||
on:click={() => deleteJob(nuiName, nuiRank, category)}>
|
||||
<svelte:component this={DeleteSVG} />
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-sm text-[var(--color-lightestgrey)]">
|
||||
{description}
|
||||
</p>
|
||||
<div class="job-details flex gap-[12px] justify-stretch">
|
||||
<JobDetail icon={SalarySVG} detail="Salary" value={salary} svgSize="w-[0.8rem]"/>
|
||||
<JobDetail icon={RankSVG} detail="Rank" value={rank} svgSize="w-[1.4rem]"/>
|
||||
<JobDetail icon={ActiveSVG} detail="Active" value={active} svgSize="w-[1.1rem]"/>
|
||||
</div>
|
||||
<div class="mt-2">
|
||||
{#if !isActive}
|
||||
<button class="bg-[var(--color-green)] flex flex-row h-11 items-center justify-center gap-1 b-rd-[5px] py-[10px] font-medium text-black flex-1 w-full"
|
||||
on:click={() => setActiveJob(nuiName, nuiName, nuiRank)}
|
||||
>
|
||||
<div class="w-4">
|
||||
<svelte:component this={SelectSVG} />
|
||||
</div>
|
||||
<p class="ml-[5px] uppercase tracking-wide">select</p>
|
||||
</button>
|
||||
{/if}
|
||||
{#if isActive}
|
||||
<div class="flex flex-row justify-between gap-2">
|
||||
<button class={"flex flex-1 flex-row gap-2 border-1 b-rd-[5px] justify-center items-center h-11"+
|
||||
(selectHover ? "border-[var(--color-orange)] text-[var(--color-orange)]":"")}
|
||||
on:click={handleUnSelectJob} on:mouseenter={handleOnSelectMouseEnter} on:mouseleave={handleOnSelectMouseLeave}>
|
||||
{#if !selectHover}
|
||||
<div class="w-5">
|
||||
<svelte:component this={SelectSVG}/>
|
||||
</div>
|
||||
{/if}
|
||||
<p class="uppercase tracking-wide">
|
||||
{selectText}
|
||||
</p>
|
||||
</button>
|
||||
<div class="flex-1">
|
||||
<button class={`flex flex-row justify-center items-center gap-1 h-11 border-1 b-rd-[5px] py-[10px] font-medium flex-1 w-full ` +
|
||||
($onDuty ?
|
||||
"border-[var(--color-green)] text-[var(--color-green)] "
|
||||
: "border-[var(--color-orange)] text-[var(--color-orange)] ")+
|
||||
($onDuty && !transitionOnDuty ? "hover:border-[var(--color-orange)] hover:text-[var(--color-orange)]":"")+
|
||||
(!$onDuty && !transitionOffDuty ? "hover:border-[var(--color-green)] hover:text-[var(--color-green)]":"")
|
||||
}
|
||||
on:click={handleDutyChange} on:mouseenter={handleOnDutyMouseEnter} on:mouseleave={handleOnDutyMouseLeave}
|
||||
>
|
||||
{#if ($onDuty && !onDutyHover) || transitionOnDuty}
|
||||
<div class="w-5">
|
||||
<svelte:component this={ClockSVG} />
|
||||
</div>
|
||||
{/if}
|
||||
{#if (!$onDuty && !onDutyHover) || transitionOffDuty}
|
||||
<div class="w-[0.9rem]">
|
||||
<svelte:component this={CrossMarkSVG} />
|
||||
</div>
|
||||
{/if}
|
||||
<p class="ml-[5px] uppercase tracking-wide">{dutyText}</p>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</main>
|
|
@ -0,0 +1,23 @@
|
|||
<script lang="ts">
|
||||
import NavItem from './atoms/NavItem.svelte';
|
||||
import PanelStore from '../stores/PanelStore';
|
||||
import type { side } from '../types/types';
|
||||
|
||||
export let side: side;
|
||||
|
||||
const { panelActive, panels } = PanelStore;
|
||||
</script>
|
||||
|
||||
<nav class="w-[80px] min-h-screen nav flex flex-col z-10">
|
||||
<div class="ps-logo w-full h-[80px]"/>
|
||||
{#each $panels as item}
|
||||
<NavItem name={item.name} isActive={item.name == $panelActive} icon={item.icon} {side}/>
|
||||
{/each}
|
||||
</nav>
|
||||
|
||||
<style>
|
||||
.nav {
|
||||
background: var(--color-darkerblue);
|
||||
border-left: 1px solid var(--color-darkblue);
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,22 @@
|
|||
<script lang="ts">
|
||||
export let icon: any = null;
|
||||
export let detail: string;
|
||||
export let value: string | number;
|
||||
export let svgSize: string;
|
||||
</script>
|
||||
|
||||
<div class="flex flex-1 flex-col items-center gap-2 b-2 b-rd-2 border-[var(--color-darkblue)] pt-[14px] pb-[14px]">
|
||||
<div class="w-full flex justify-center text-white">
|
||||
<div class={svgSize}>
|
||||
<svelte:component this={icon}/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<p class="text-xs">
|
||||
{detail}:
|
||||
<span class="text-[var(--color-green)]">
|
||||
{value}
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,38 @@
|
|||
<script lang="ts">
|
||||
import PanelStore from "../../stores/PanelStore";
|
||||
import type { side } from '../../types/types';
|
||||
|
||||
export let icon: any;
|
||||
export let isActive: boolean;
|
||||
export let name: string;
|
||||
export let side: side;
|
||||
|
||||
function navItemClicked(item: string): void {
|
||||
if (isActive) {
|
||||
PanelStore.setActive("");
|
||||
} else {
|
||||
PanelStore.setActive(item);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class={"navitem w-full h-[60px] flex justify-center items-center cursor-pointer duration-200 "+
|
||||
(side == "left" ? "border-l-4 " : "border-r-4 ")+
|
||||
(isActive ? side == "left" ? "border-l-[var(--color-green)] bg-[var(--color-darkestblue)] ": "border-r-[var(--color-green)] bg-[var(--color-darkestblue)] "
|
||||
: side == "left" ? "border-l-transparent ": "border-r-transparent ")}
|
||||
on:click={() => navItemClicked(name)}
|
||||
>
|
||||
<div class="icon">
|
||||
<svelte:component this={icon} color={isActive ? "var(--color-green)" : "var(--color-grey)"}/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.icon {
|
||||
width: 40%;
|
||||
color: var(--color-lightestgrey);
|
||||
}
|
||||
.navitem:hover {
|
||||
background-color: var(--color-darkestblue);
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,5 @@
|
|||
<svg fill="currentColor" viewBox="0 0 448 512">
|
||||
<path d="M224 256c70.7 0 128-57.31 128-128s-57.3-128-128-128C153.3 0 96 57.31 96 128S153.3 256 224 256zM274.7 304H173.3C77.61
|
||||
304 0 381.6 0 477.3c0 19.14 15.52 34.67 34.66 34.67h378.7C432.5 512 448 496.5 448 477.3C448 381.6 370.4 304 274.7 304z"
|
||||
/>
|
||||
</svg>
|
After Width: | Height: | Size: 311 B |
|
@ -0,0 +1,7 @@
|
|||
<script lang="ts">
|
||||
export let color: string = "black";
|
||||
</script>
|
||||
|
||||
<svg fill={color} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512">
|
||||
<path d="M0 32v448h448V32H0zm316.5 325.2L224 445.9l-92.5-88.7 64.5-184-64.5-86.6h184.9L252 173.2l64.5 184z"/>
|
||||
</svg>
|
|
@ -0,0 +1,6 @@
|
|||
<svg fill="currentColor" viewBox="0 0 512 512">
|
||||
<path d="M256 512C114.6 512 0 397.4 0 256S114.6 0 256 0S512 114.6 512 256s-114.6 256-256 256zM232 120V256c0
|
||||
8 4 15.5 10.7 20l96 64c11 7.4 25.9 4.4 33.3-6.7s4.4-25.9-6.7-33.3L280 243.2V120c0-13.3-10.7-24-24-24s-24
|
||||
10.7-24 24z"
|
||||
/>
|
||||
</svg>
|
After Width: | Height: | Size: 295 B |
|
@ -0,0 +1,5 @@
|
|||
<svg fill="currentColor" viewBox="0 0 320 512">
|
||||
<path d="M310.6 150.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L160 210.7 54.6 105.4c-12.5-12.5-32.8-12.5-45.3
|
||||
0s-12.5 32.8 0 45.3L114.7 256 9.4 361.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L160 301.3 265.4 406.6c12.5
|
||||
12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L205.3 256 310.6 150.6z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 355 B |
|
@ -0,0 +1,4 @@
|
|||
<svg fill="currentColor" viewBox="0 0 24 24">
|
||||
<path d="M0 0h24v24H0z" fill="none"/>
|
||||
<path d="M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 184 B |
|
@ -0,0 +1,14 @@
|
|||
<svg fill="currentColor" viewBox="0 0 576 512">
|
||||
<path d="M0 48C0 21.49 21.49 0 48 0H336C362.5 0 384 21.49 384 48V207L341.6 224H272C263.2 224 256 231.2 256
|
||||
240V304C256 304.9 256.1 305.7 256.2 306.6C258.5 364.7 280.3 451.4 354.9 508.1C349.1 510.6 342.7 512 336 512H240V432C240
|
||||
405.5 218.5 384 192 384C165.5 384 144 405.5 144 432V512H48C21.49 512 0 490.5 0 464V48zM80 224C71.16 224 64 231.2 64 240V272C64
|
||||
280.8 71.16 288 80 288H112C120.8 288 128 280.8 128 272V240C128 231.2 120.8 224 112 224H80zM160 272C160 280.8 167.2 288 176
|
||||
288H208C216.8 288 224 280.8 224 272V240C224 231.2 216.8 224 208 224H176C167.2 224 160 231.2 160 240V272zM64 144C64 152.8 71.16
|
||||
160 80 160H112C120.8 160 128 152.8 128 144V112C128 103.2 120.8 96 112 96H80C71.16 96 64 103.2 64 112V144zM176 96C167.2 96 160
|
||||
103.2 160 112V144C160 152.8 167.2 160 176 160H208C216.8 160 224 152.8 224 144V112C224 103.2 216.8 96 208 96H176zM256 144C256
|
||||
152.8 263.2 160 272 160H304C312.8 160 320 152.8 320 144V112C320 103.2 312.8 96 304 96H272C263.2 96 256 103.2 256 112V144zM423.1
|
||||
225.7C428.8 223.4 435.2 223.4 440.9 225.7L560.9 273.7C570 277.4 576 286.2 576 296C576 359.3 550.1 464.8 441.2 510.2C435.3 512.6
|
||||
428.7 512.6 422.8 510.2C313.9 464.8 288 359.3 288 296C288 286.2 293.1 277.4 303.1 273.7L423.1 225.7zM432 273.8V461.7C500.2 428.7
|
||||
523.5 362.7 527.4 311.1L432 273.8z"
|
||||
/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.3 KiB |
|
@ -0,0 +1,10 @@
|
|||
<svg fill="currentColor" viewBox="0 0 576 512">
|
||||
<path d="M287.9 0C297.1 0 305.5 5.25 309.5 13.52L378.1 154.8L531.4 177.5C540.4 178.8 547.8 185.1 550.7 193.7C553.5 202.4
|
||||
551.2 211.9 544.8 218.2L433.6 328.4L459.9 483.9C461.4 492.9 457.7 502.1 450.2 507.4C442.8 512.7 432.1 513.4 424.9 509.1L287.9
|
||||
435.9L150.1 509.1C142.9 513.4 133.1 512.7 125.6 507.4C118.2 502.1 114.5 492.9 115.1 483.9L142.2 328.4L31.11 218.2C24.65 211.9
|
||||
22.36 202.4 25.2 193.7C28.03 185.1 35.5 178.8 44.49 177.5L197.7 154.8L266.3 13.52C270.4 5.249 278.7 0 287.9 0L287.9 0zM287.9
|
||||
78.95L235.4 187.2C231.9 194.3 225.1 199.3 217.3 200.5L98.98 217.9L184.9 303C190.4 308.5 192.9 316.4 191.6 324.1L171.4 443.7L276.6
|
||||
387.5C283.7 383.7 292.2 383.7 299.2 387.5L404.4 443.7L384.2 324.1C382.9 316.4 385.5 308.5 391 303L476.9 217.9L358.6 200.5C350.7
|
||||
199.3 343.9 194.3 340.5 187.2L287.9 78.95z"
|
||||
/>
|
||||
</svg>
|
After Width: | Height: | Size: 885 B |
|
@ -0,0 +1,13 @@
|
|||
<svg fill="currentColor" viewBox="0 0 320 512">
|
||||
<path d="M160 0C177.7 0 192 14.33 192 32V67.68C193.6 67.89 195.1 68.12 196.7 68.35C207.3 69.93 238.9 75.02 251.9 78.31C268.1
|
||||
82.65 279.4 100.1 275 117.2C270.7 134.3 253.3 144.7 236.1 140.4C226.8 137.1 198.5 133.3 187.3 131.7C155.2 126.9 127.7 129.3
|
||||
108.8 136.5C90.52 143.5 82.93 153.4 80.92 164.5C78.98 175.2 80.45 181.3 82.21 185.1C84.1 189.1 87.79 193.6 95.14 198.5C111.4
|
||||
209.2 136.2 216.4 168.4 225.1L171.2 225.9C199.6 233.6 234.4 243.1 260.2 260.2C274.3 269.6 287.6 282.3 295.8 299.9C304.1 317.7
|
||||
305.9 337.7 302.1 358.1C295.1 397 268.1 422.4 236.4 435.6C222.8 441.2 207.8 444.8 192 446.6V480C192 497.7 177.7 512 160 512C142.3
|
||||
512 128 497.7 128 480V445.1C127.6 445.1 127.1 444.1 126.7 444.9L126.5 444.9C102.2 441.1 62.07 430.6 35 418.6C18.85 411.4 11.58
|
||||
392.5 18.76 376.3C25.94 360.2 44.85 352.9 60.1 360.1C81.9 369.4 116.3 378.5 136.2 381.6C168.2 386.4 194.5 383.6 212.3 376.4C229.2
|
||||
369.5 236.9 359.5 239.1 347.5C241 336.8 239.6 330.7 237.8 326.9C235.9 322.9 232.2 318.4 224.9 313.5C208.6 302.8 183.8 295.6 151.6
|
||||
286.9L148.8 286.1C120.4 278.4 85.58 268.9 59.76 251.8C45.65 242.4 32.43 229.7 24.22 212.1C15.89 194.3 14.08 174.3 17.95 153C25.03
|
||||
114.1 53.05 89.29 85.96 76.73C98.98 71.76 113.1 68.49 128 66.73V32C128 14.33 142.3 0 160 0V0z"
|
||||
/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.3 KiB |
|
@ -0,0 +1,7 @@
|
|||
<svg fill="currentColor" x="0px" y="0px" viewBox="0 0 562.7 502.7" xml:space="preserve">
|
||||
<path d="M547.3,112.1c-98,98.1-196,196.2-294,294.2c-11.9,11.9-23.7,23.8-35.6,35.6c-6,6-9.3,6-15.3,0
|
||||
C141.5,381.1,80.7,320.2,19.8,259.3c-6-6-6-9.2,0-15.2c13.6-13.6,27.2-27.2,40.8-40.8c6-6,9.2-6,15.2,0
|
||||
c43.2,43.2,86.5,86.4,129.7,129.7c1.3,1.3,2.4,3,3.8,4.8c2-1.9,3.4-3.1,4.7-4.4c90.5-90.5,181-181,271.5-271.5
|
||||
c7.7-7.7,10-7.7,17.8,0.1c12.7,12.7,25.5,25.5,38.2,38.2c2.1,2.1,3.9,4.4,5.8,6.6C547.3,108.6,547.3,110.4,547.3,112.1z"
|
||||
/>
|
||||
</svg>
|
After Width: | Height: | Size: 539 B |
|
@ -0,0 +1,10 @@
|
|||
<svg fill="currentColor" viewBox="0 0 576 512">
|
||||
<path d="M352 0C369.7 0 384 14.33 384 32V64L384 64.15C422.6 66.31 456.3 91.49 469.2 128.3L504.4 228.8C527.6 238.4 544
|
||||
261.3 544 288V480C544 497.7 529.7 512 512 512H480C462.3 512 448 497.7 448 480V432H128V480C128 497.7 113.7 512 96 512H64C46.33
|
||||
512 32 497.7 32 480V288C32 261.3 48.36 238.4 71.61 228.8L106.8 128.3C119.7 91.49 153.4 66.31 192 64.15L192 64V32C192 14.33
|
||||
206.3 0 224 0L352 0zM197.4 128C183.8 128 171.7 136.6 167.2 149.4L141.1 224H434.9L408.8 149.4C404.3 136.6 392.2 128 378.6
|
||||
128H197.4zM128 352C145.7 352 160 337.7 160 320C160 302.3 145.7 288 128 288C110.3 288 96 302.3 96 320C96 337.7 110.3 352
|
||||
128 352zM448 288C430.3 288 416 302.3 416 320C416 337.7 430.3 352 448 352C465.7 352 480 337.7 480 320C480 302.3 465.7 288
|
||||
448 288z"
|
||||
/>
|
||||
</svg>
|
After Width: | Height: | Size: 824 B |
|
@ -0,0 +1,16 @@
|
|||
<script lang="ts">
|
||||
export let color: string = "black";
|
||||
</script>
|
||||
|
||||
<svg fill={color} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
|
||||
<path d="M152.1 38.16C161.9 47.03 162.7 62.2 153.8 72.06L81.84 152.1C77.43 156.9 71.21 159.8 64.63 159.1C58.05
|
||||
160.2 51.69 157.6 47.03 152.1L7.029 112.1C-2.343 103.6-2.343 88.4 7.029 79.03C16.4 69.66 31.6 69.66 40.97 79.03L63.08
|
||||
101.1L118.2 39.94C127 30.09 142.2 29.29 152.1 38.16V38.16zM152.1 198.2C161.9 207 162.7 222.2 153.8 232.1L81.84 312.1C77.43
|
||||
316.9 71.21 319.8 64.63 319.1C58.05 320.2 51.69 317.6 47.03 312.1L7.029 272.1C-2.343 263.6-2.343 248.4 7.029 239C16.4
|
||||
229.7 31.6 229.7 40.97 239L63.08 261.1L118.2 199.9C127 190.1 142.2 189.3 152.1 198.2V198.2zM224 96C224 78.33 238.3 64
|
||||
256 64H480C497.7 64 512 78.33 512 96C512 113.7 497.7 128 480 128H256C238.3 128 224 113.7 224 96V96zM224 256C224 238.3
|
||||
238.3 224 256 224H480C497.7 224 512 238.3 512 256C512 273.7 497.7 288 480 288H256C238.3 288 224 273.7 224 256zM160 416C160
|
||||
398.3 174.3 384 192 384H480C497.7 384 512 398.3 512 416C512 433.7 497.7 448 480 448H192C174.3 448 160 433.7 160 416zM0
|
||||
416C0 389.5 21.49 368 48 368C74.51 368 96 389.5 96 416C96 442.5 74.51 464 48 464C21.49 464 0 442.5 0 416z"
|
||||
/>
|
||||
</svg>
|
|
@ -0,0 +1,9 @@
|
|||
import App from './App.svelte'
|
||||
import 'uno.css'
|
||||
import '@unocss/reset/tailwind.css'
|
||||
|
||||
const app = new App({
|
||||
target: document.getElementById('app')
|
||||
})
|
||||
|
||||
export default app
|
|
@ -0,0 +1,120 @@
|
|||
import { writable, Writable, get } from "svelte/store";
|
||||
import fetchNUI from '../utils/fetch';
|
||||
import type { Job, JobManifest, side, nuiUpdateJobMessage } from '../types/types';
|
||||
import PanelStore from "./PanelStore";
|
||||
|
||||
export interface nuiOpenMessage {
|
||||
activeJob: string;
|
||||
onDuty: boolean;
|
||||
jobs: JobManifest;
|
||||
side: side;
|
||||
}
|
||||
|
||||
interface JobState {
|
||||
jobManifest: Writable<JobManifest>;
|
||||
activeJob: Writable<string>;
|
||||
onDuty: Writable<boolean>;
|
||||
}
|
||||
|
||||
const store = () => {
|
||||
const JobStore: JobState = {
|
||||
jobManifest: writable({
|
||||
"civilian": [],
|
||||
"whitelist": [],
|
||||
}),
|
||||
activeJob: writable("police person"),
|
||||
onDuty: writable(false),
|
||||
}
|
||||
|
||||
const methods = {
|
||||
deleteJob(nuiName: string, nuiRank: number, category: string) {
|
||||
fetchNUI("removejob", {
|
||||
name: nuiName,
|
||||
grade: nuiRank,
|
||||
});
|
||||
// Remove job from list
|
||||
JobStore.jobManifest.update((state) => {
|
||||
state[category] = state[category].filter((element: Job) => element.name != nuiName);
|
||||
return state;
|
||||
});
|
||||
},
|
||||
receiveOpenMessage(data: nuiOpenMessage) {
|
||||
JobStore.jobManifest.set(data.jobs);
|
||||
JobStore.activeJob.set(data.activeJob);
|
||||
JobStore.onDuty.set(data.onDuty);
|
||||
PanelStore.side.set(data.side || "right");
|
||||
},
|
||||
recieveUpdateJob(data: nuiUpdateJobMessage) {
|
||||
const activeJob: string = get(JobStore.activeJob);
|
||||
if (activeJob == data.name) {
|
||||
JobStore.onDuty.set(data.onDuty);
|
||||
}
|
||||
|
||||
JobStore.jobManifest.update((state) => {
|
||||
function updateJob(kind: "whitelist" | "civilian", index: number) {
|
||||
let changeJob = state[kind][index];
|
||||
changeJob.grade = data.grade;
|
||||
changeJob.gradeLabel = data.gradeLabel;
|
||||
changeJob.salary = data.salary;
|
||||
}
|
||||
|
||||
function newJob(): Job {
|
||||
return {
|
||||
name: data.name,
|
||||
label: data.label,
|
||||
description: data.description,
|
||||
salary: data.salary,
|
||||
gradeLabel: data.gradeLabel,
|
||||
grade: data.grade,
|
||||
active: 0,
|
||||
icon: data.icon,
|
||||
}
|
||||
}
|
||||
|
||||
let findSameName = (job: Job) => {
|
||||
return job.name == data.name
|
||||
}
|
||||
|
||||
const accessString: "civilian" | "whitelist" = data.isWhitelist ? "whitelist" : "civilian";
|
||||
let index = state[accessString]?.findIndex(findSameName);
|
||||
|
||||
if (index != -1) {
|
||||
updateJob(accessString, index);
|
||||
} else {
|
||||
state[accessString] = [...state[accessString], newJob()]
|
||||
}
|
||||
|
||||
return state;
|
||||
})
|
||||
},
|
||||
async setActiveJob(jobName: string, nuiName: string, nuiRank: number) {
|
||||
JobStore.activeJob.set(jobName);
|
||||
// Needs to give back onDuty
|
||||
let data = await fetchNUI("selectjob", {
|
||||
name: nuiName,
|
||||
grade: nuiRank,
|
||||
});
|
||||
JobStore.onDuty.set(data?.onDuty);
|
||||
},
|
||||
unSetActiveJob() {
|
||||
JobStore.activeJob.set("");
|
||||
JobStore.onDuty.set(false);
|
||||
// Unselect current job by setting player to unemployed
|
||||
fetchNUI("selectjob", {
|
||||
name: 'unemployed',
|
||||
grade: 0,
|
||||
});
|
||||
},
|
||||
toggleDuty() {
|
||||
JobStore.onDuty.update(state => !state);
|
||||
fetchNUI('toggleduty', null);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
...JobStore,
|
||||
...methods
|
||||
}
|
||||
}
|
||||
|
||||
export default store();
|
|
@ -0,0 +1,62 @@
|
|||
import { writable, Writable } from "svelte/store";
|
||||
import type { side } from '../types/types';
|
||||
import fetchNUI from '../utils/fetch';
|
||||
import CivilianSVG from '../components/atoms/svgs/CivilianSVG.svelte';
|
||||
import WhiteListSVG from '../components/atoms/svgs/WhitelistSVG.svelte';
|
||||
|
||||
interface panel {
|
||||
name: string;
|
||||
icon: any;
|
||||
}
|
||||
|
||||
interface PanelState {
|
||||
show: Writable<boolean>;
|
||||
panelActive: Writable<string>;
|
||||
panels: Writable<Array<panel>>;
|
||||
side: Writable<side>;
|
||||
}
|
||||
|
||||
const panels: Array<panel> = [
|
||||
{
|
||||
name: "whitelist",
|
||||
icon: WhiteListSVG,
|
||||
},
|
||||
{
|
||||
name: "civilian",
|
||||
icon: CivilianSVG,
|
||||
}
|
||||
]
|
||||
|
||||
const store = () => {
|
||||
const PanelStore: PanelState = {
|
||||
panelActive: writable(""),
|
||||
panels: writable(panels),
|
||||
show: writable(false),
|
||||
side: writable("right"),
|
||||
}
|
||||
|
||||
const methods = {
|
||||
handleKeyUp(event: KeyboardEvent) {
|
||||
if (event.key == "Escape") {
|
||||
methods.setShow(false);
|
||||
fetchNUI("closemenu", null);
|
||||
}
|
||||
},
|
||||
setActive(name: string) {
|
||||
PanelStore.panelActive.set(name);
|
||||
},
|
||||
setShow(show: boolean) {
|
||||
PanelStore.show.set(show);
|
||||
},
|
||||
setSide(side: side) {
|
||||
PanelStore.side.set(side);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
...PanelStore,
|
||||
...methods
|
||||
}
|
||||
}
|
||||
|
||||
export default store();
|
|
@ -0,0 +1,2 @@
|
|||
const debugMode: boolean = import.meta.env.DEV;
|
||||
export default debugMode;
|
|
@ -0,0 +1,7 @@
|
|||
import { describe, expect, test } from 'vitest';
|
||||
|
||||
describe('function()', async () => {
|
||||
test('behavior', () => {
|
||||
expect(1).toBe(1);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,22 @@
|
|||
export interface Job {
|
||||
name: string;
|
||||
label: string;
|
||||
description: string;
|
||||
salary: number;
|
||||
gradeLabel: string;
|
||||
grade: number;
|
||||
active: number;
|
||||
icon: string;
|
||||
}
|
||||
|
||||
export interface nuiUpdateJobMessage extends Omit<Job, "active"> {
|
||||
isWhitelist: boolean;
|
||||
onDuty: boolean;
|
||||
}
|
||||
|
||||
export interface JobManifest {
|
||||
"whitelist": Array<Job>;
|
||||
"civilian": Array<Job>;
|
||||
}
|
||||
|
||||
export type side = "left" | "right";
|
|
@ -0,0 +1,33 @@
|
|||
import { onMount, onDestroy } from "svelte";
|
||||
import JobStore from '../stores/JobStore';
|
||||
import PanelStore from '../stores/PanelStore';
|
||||
|
||||
interface nuiMessage {
|
||||
data: {
|
||||
action: string,
|
||||
[key: string]: any,
|
||||
},
|
||||
}
|
||||
|
||||
export function EventHandler() {
|
||||
function mainEvent(event: nuiMessage) {
|
||||
switch (event.data.action) {
|
||||
case "sendjobs":
|
||||
JobStore.receiveOpenMessage(event.data as any);
|
||||
PanelStore.setShow(true);
|
||||
break;
|
||||
case "updatejob":
|
||||
JobStore.recieveUpdateJob(event.data as any);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
onMount(() => window.addEventListener("message", mainEvent));
|
||||
onDestroy(() => window.removeEventListener("message", mainEvent));
|
||||
}
|
||||
|
||||
export function handleKeyUp(event: KeyboardEvent) {
|
||||
const charCode = event.key;
|
||||
if (charCode == "Escape") {
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
export default async function fetchNui(eventName: string, data: unknown = {}) {
|
||||
const options = {
|
||||
method: "post",
|
||||
headers: {
|
||||
"Content-Type": "application/json; charset=UTF-8",
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
};
|
||||
|
||||
const getResourceName = () => {
|
||||
try {
|
||||
// @ts-ignore
|
||||
return window.GetParentResourceName();
|
||||
} catch(err) {
|
||||
return "ps-multijob";
|
||||
}
|
||||
}
|
||||
|
||||
const resourceName = getResourceName();
|
||||
|
||||
try {
|
||||
const resp = await fetch(`https://${resourceName}/${eventName}`, options);
|
||||
return await resp.json();
|
||||
} catch(err) {
|
||||
}
|
||||
}
|
|
@ -0,0 +1,132 @@
|
|||
import type { JobManifest } from '../types/types';
|
||||
import type { nuiOpenMessage } from '../stores/JobStore';
|
||||
|
||||
export default function mockEventCall(data: unknown = {}) {
|
||||
window.dispatchEvent(
|
||||
new MessageEvent("message", {data})
|
||||
);
|
||||
};
|
||||
|
||||
export function exampleCall() {
|
||||
setTimeout(() => {
|
||||
mockEventCall({
|
||||
action: 'show',
|
||||
data: {
|
||||
header: "Some Header!",
|
||||
},
|
||||
});
|
||||
}, 100);
|
||||
};
|
||||
|
||||
export function mockJobMenuOpen() {
|
||||
const mockJobManifest: JobManifest = {
|
||||
"whitelist": [
|
||||
{
|
||||
name: "police person",
|
||||
label: "police person",
|
||||
description: `Generate Lorem lpsum placeholder text.
|
||||
Select the number of characters, words, sentences or paragraphs, and hit generate!`,
|
||||
salary: 250,
|
||||
gradeLabel: "Regular",
|
||||
grade: 0,
|
||||
active: 0,
|
||||
icon: "fa-solid fa-trash-can",
|
||||
},
|
||||
{
|
||||
name: "police chief",
|
||||
label: "police chief",
|
||||
description: "Blah blah blah",
|
||||
salary: 500,
|
||||
gradeLabel: "Boss",
|
||||
grade: 0,
|
||||
active: 1,
|
||||
icon: "",
|
||||
},
|
||||
{
|
||||
name: "police chief2",
|
||||
label: "police chief2",
|
||||
description: "Blah blah blah",
|
||||
salary: 500,
|
||||
gradeLabel: "Boss",
|
||||
grade: 0,
|
||||
active: 1,
|
||||
icon: "",
|
||||
},
|
||||
{
|
||||
name: "police chief3",
|
||||
label: "police chief3",
|
||||
description: "Blah blah blah",
|
||||
salary: 500,
|
||||
gradeLabel: "Boss",
|
||||
grade: 0,
|
||||
active: 1,
|
||||
icon: "",
|
||||
},
|
||||
{
|
||||
name: "police chief4",
|
||||
label: "police chief4",
|
||||
description: "Blah blah blah",
|
||||
salary: 500,
|
||||
gradeLabel: "Boss",
|
||||
grade: 0,
|
||||
active: 1,
|
||||
icon: "",
|
||||
},
|
||||
],
|
||||
"civilian": [
|
||||
{
|
||||
name: "taxi driver",
|
||||
label: "taxi driver",
|
||||
description: `Generate Lorem lpsum placeholder text.
|
||||
Select the number of characters, words, sentences or paragraphs, and hit generate!`,
|
||||
salary: 150,
|
||||
gradeLabel: "Regular",
|
||||
grade: 0,
|
||||
active: 0,
|
||||
icon: "",
|
||||
},
|
||||
{
|
||||
name: "murdershot1",
|
||||
label: "murdershot1",
|
||||
description: "Take people's order and serve them food",
|
||||
salary: 100,
|
||||
gradeLabel: "Cashier",
|
||||
grade: 0,
|
||||
active: 0,
|
||||
icon: "",
|
||||
},
|
||||
{
|
||||
name: "murdershot2",
|
||||
label: "murdershot2",
|
||||
description: "Take people's order and serve them food",
|
||||
salary: 100,
|
||||
gradeLabel: "Cashier",
|
||||
grade: 0,
|
||||
active: 0,
|
||||
icon: "",
|
||||
},
|
||||
{
|
||||
name: "murdershot3",
|
||||
label: "murdershot3",
|
||||
description: "Take people's order and serve them food",
|
||||
salary: 100,
|
||||
gradeLabel: "Cashier",
|
||||
grade: 0,
|
||||
active: 0,
|
||||
icon: "",
|
||||
}
|
||||
],
|
||||
}
|
||||
setTimeout(() => {
|
||||
let sendData: nuiOpenMessage = {
|
||||
activeJob: "murdershot1",
|
||||
jobs: mockJobManifest,
|
||||
onDuty: true,
|
||||
side: "right",
|
||||
}
|
||||
mockEventCall({
|
||||
action: 'sendjobs',
|
||||
...sendData,
|
||||
});
|
||||
}, 1000);
|
||||
}
|
2
resources/[standalone]/ps-multijob/svelte-source/src/vite-env.d.ts
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
/// <reference types="svelte" />
|
||||
/// <reference types="vite/client" />
|
|
@ -0,0 +1,7 @@
|
|||
import sveltePreprocess from 'svelte-preprocess'
|
||||
|
||||
export default {
|
||||
// Consult https://github.com/sveltejs/svelte-preprocess
|
||||
// for more information about preprocessors
|
||||
preprocess: sveltePreprocess()
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"extends": "@tsconfig/svelte/tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"target": "esnext",
|
||||
"useDefineForClassFields": true,
|
||||
"module": "esnext",
|
||||
"resolveJsonModule": true,
|
||||
"baseUrl": ".",
|
||||
/**
|
||||
* Typecheck JS in `.svelte` and `.js` files by default.
|
||||
* Disable checkJs if you'd like to use dynamic types in JS.
|
||||
* Note that setting allowJs false does not prevent the use
|
||||
* of JS in `.svelte` files.
|
||||
*/
|
||||
"allowJs": true,
|
||||
"checkJs": true,
|
||||
"isolatedModules": true
|
||||
},
|
||||
"include": ["src/**/*.d.ts", "src/**/*.ts", "src/**/*.js", "src/**/*.svelte"],
|
||||
"references": [{ "path": "./tsconfig.node.json" }]
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node"
|
||||
},
|
||||
"include": ["vite.config.ts"]
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
/// <reference types="vitest" />
|
||||
import { defineConfig } from 'vite'
|
||||
import { svelte } from '@sveltejs/vite-plugin-svelte'
|
||||
import { minify } from "html-minifier";
|
||||
import Unocss from 'unocss/vite'
|
||||
import presetUno from '@unocss/preset-uno'
|
||||
|
||||
const minifyHtml = () => {
|
||||
return {
|
||||
name: 'html-transform',
|
||||
transformIndexHtml(html) {
|
||||
return minify(html, {
|
||||
collapseWhitespace: true,
|
||||
});
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export default defineConfig(({ mode }) => {
|
||||
const isProduction = mode === 'production';
|
||||
|
||||
return {
|
||||
plugins: [
|
||||
Unocss({
|
||||
presets: [ presetUno() ],
|
||||
}),
|
||||
svelte(),
|
||||
isProduction && minifyHtml(),
|
||||
],
|
||||
test: {
|
||||
globals: true,
|
||||
environment: 'jsdom',
|
||||
},
|
||||
base: './', // fivem nui needs to have local dir reference
|
||||
build: {
|
||||
minify: isProduction,
|
||||
emptyOutDir: true,
|
||||
outDir: '../html',
|
||||
assetsDir: './',
|
||||
rollupOptions: {
|
||||
output: {
|
||||
// By not having hashes in the name, you don't have to update the manifest, yay!
|
||||
entryFileNames: `[name].js`,
|
||||
chunkFileNames: `[name].js`,
|
||||
assetFileNames: `[name].[ext]`
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
});
|