Compare commits

...
This repository has been archived on 2024-08-06. You can view files and clone it, but cannot push or open issues or pull requests.

24 commits

Author SHA1 Message Date
a458f1d600
Rework Header Component for Auth handling 2023-12-11 20:16:20 +01:00
c571f084d7
Add Input Component from shadcn 2023-12-11 20:16:04 +01:00
01e301eb60
Implemented Mode Select and FIlters for Admin Page 2023-12-10 23:21:54 +01:00
4d6f3c23c3
Add Shadcn Components 2023-12-10 23:21:35 +01:00
cf477b1f25
Updates to Navbar Account PopOver 2023-12-10 23:21:13 +01:00
792b11248a
Package.json Version Bump 2023-12-10 23:19:42 +01:00
79bdf1bc6c
Use entire site height by default 2023-12-09 18:54:09 +01:00
bbe681d6d9
Svelte 5 Version Bump (fixed #each block problems) 2023-12-09 18:54:00 +01:00
0ac6065a83
Svelte Rune Mode fixes 2023-12-09 18:53:45 +01:00
59a0cb4bc9
Temporary Auth disable on Admin page for testing 2023-12-09 18:53:07 +01:00
e2bbc2f1eb
Admin "new" route auth checking 2023-12-09 18:52:53 +01:00
c0c38e7aa7
Added several UI components from shadcn 2023-12-09 18:52:40 +01:00
17e52b12d0
Reworked Navbar to use shadcn 2023-12-06 21:02:07 +01:00
69574ae124
shadcn UI config changes 2023-12-06 21:01:54 +01:00
fb4425a20c
Removed Various orphan packages, added UI yarn alias 2023-12-06 21:01:39 +01:00
596b9d7f79
Moved Account to Profile 2023-12-06 21:01:02 +01:00
3fd683b6ee
Add shadcn UI 2023-12-06 21:00:46 +01:00
8da05a33cb
Move Components to new Directory 2023-12-06 21:00:38 +01:00
4eef07de01
Dummy content 2023-12-05 20:27:38 +01:00
fdbb265ae6
Modify some lib components for Svelte 5 2023-12-05 19:25:34 +01:00
f0c66fb4a2
move Svelte-4 components to legacy route 2023-12-05 19:25:14 +01:00
47aadc9ef5
Rework Top Level Components to not use Skeleton 2023-12-05 19:24:56 +01:00
59c2fd8f2d
Remove most of Skeleton-UI 2023-12-05 19:24:27 +01:00
33c3eb5581
Package Version Bump. Now Using Svelte 5 Preview 2023-12-04 16:18:12 +01:00
107 changed files with 2590 additions and 555 deletions

3
.gitignore vendored
View file

@ -11,4 +11,5 @@ node_modules
vite.config.js.timestamp-* vite.config.js.timestamp-*
vite.config.ts.timestamp-* vite.config.ts.timestamp-*
/.vscode /.vscode
/.idea

13
components.json Normal file
View file

@ -0,0 +1,13 @@
{
"$schema": "https://shadcn-svelte.com/schema.json",
"style": "new-york",
"tailwind": {
"config": "tailwind.config.js",
"css": "src/app.postcss",
"baseColor": "slate"
},
"aliases": {
"components": "$lib/components",
"utils": "$lib/utils"
}
}

View file

@ -9,40 +9,44 @@
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
"lint": "prettier --plugin-search-dir . --check . && eslint .", "lint": "prettier --plugin-search-dir . --check . && eslint .",
"format": "prettier --plugin-search-dir . --write ." "format": "prettier --plugin-search-dir . --write .",
"ui": "npx shadcn-svelte@latest"
}, },
"devDependencies": { "devDependencies": {
"@fontsource/fira-mono": "^4.5.10",
"@neoconfetti/svelte": "^1.0.0",
"@sveltejs/adapter-auto": "^2.0.0", "@sveltejs/adapter-auto": "^2.0.0",
"@sveltejs/kit": "^1.20.4", "@sveltejs/kit": "^1.20.4",
"@types/cookie": "^0.5.1",
"@types/node": "^20.6.0",
"@typescript-eslint/eslint-plugin": "^5.45.0", "@typescript-eslint/eslint-plugin": "^5.45.0",
"@typescript-eslint/parser": "^5.45.0", "@typescript-eslint/parser": "^5.45.0",
"autoprefixer": "^10.4.15",
"eslint": "^8.28.0", "eslint": "^8.28.0",
"eslint-config-prettier": "^8.5.0", "eslint-config-prettier": "^8.5.0",
"eslint-plugin-svelte": "^2.30.0", "eslint-plugin-svelte": "^2.30.0",
"postcss": "^8.4.29",
"postcss-load-config": "^4.0.1",
"prettier": "^2.8.0", "prettier": "^2.8.0",
"prettier-plugin-svelte": "^2.10.1", "prettier-plugin-svelte": "^2.10.1",
"svelte": "^4.0.5", "svelte": "^5.0.0-next.17",
"svelte-check": "^3.4.3", "svelte-check": "^3.4.3",
"svelte-loading-spinners": "^0.3.4",
"tslib": "^2.4.1", "tslib": "^2.4.1",
"typescript": "^5.0.0", "typescript": "^5.0.0",
"vite": "^4.4.2", "vite": "^4.4.2"
"postcss": "8.4.29",
"autoprefixer": "10.4.15",
"tailwindcss": "3.3.3",
"@skeletonlabs/skeleton": "2.0.0",
"@skeletonlabs/tw-plugin": "0.1.0",
"vite-plugin-tailwind-purgecss": "0.1.3",
"@types/node": "20.6.0",
"@fontsource/fira-mono": "^4.5.10",
"@neoconfetti/svelte": "^1.0.0",
"@types/cookie": "^0.5.1",
"postcss-load-config": "^4.0.1",
"svelte-loading-spinners": "^0.3.4"
}, },
"type": "module", "type": "module",
"dependencies": { "dependencies": {
"chart.js": "^4.3.3", "bits-ui": "^0.11.6",
"chartjs-plugin-datalabels": "^2.2.0", "chart.js": "^4.3.3",
"svelte-chartjs": "^3.1.2" "chartjs-plugin-datalabels": "^2.2.0",
} "clsx": "^2.0.0",
} "flowbite": "^2.2.0",
"radix-icons-svelte": "^1.2.1",
"svelte-chartjs": "^3.1.2",
"tailwind-merge": "^2.1.0",
"tailwind-variants": "^0.1.18",
"tailwindcss": "^3.3.6"
}
}

View file

@ -1,12 +1,12 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en" class="dark"> <html lang="en" class="dark h-full">
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<link rel="icon" href="%sveltekit.assets%/favicon.png" /> <link rel="icon" href="%sveltekit.assets%/favicon.png" />
<meta name="viewport" content="width=device-width" /> <meta name="viewport" content="width=device-width" />
%sveltekit.head% %sveltekit.head%
</head> </head>
<body data-sveltekit-preload-data="hover" data-theme="wintry"> <body class="h-full w-full" data-sveltekit-preload-data="hover">
<div style="display: contents" class="h-screen overflow-hidden contents">%sveltekit.body%</div> %sveltekit.body%
</body> </body>
</html> </html>

View file

@ -1,6 +1,78 @@
@tailwind base; @tailwind base;
@tailwind components; @tailwind components;
@tailwind utilities; @tailwind utilities;
@tailwind variants;
@layer base {
html, body { @apply h-full overflow-hidden; } :root {
--background: 0 0% 100%;
--foreground: 222.2 84% 4.9%;
--muted: 210 40% 96.1%;
--muted-foreground: 215.4 16.3% 46.9%;
--popover: 0 0% 100%;
--popover-foreground: 222.2 84% 4.9%;
--card: 0 0% 100%;
--card-foreground: 222.2 84% 4.9%;
--border: 214.3 31.8% 91.4%;
--input: 214.3 31.8% 91.4%;
--primary: 222.2 47.4% 11.2%;
--primary-foreground: 210 40% 98%;
--secondary: 210 40% 96.1%;
--secondary-foreground: 222.2 47.4% 11.2%;
--accent: 210 40% 96.1%;
--accent-foreground: 222.2 47.4% 11.2%;
--destructive: 0 72.2% 50.6%;
--destructive-foreground: 210 40% 98%;
--ring: 222.2 84% 4.9%;
--radius: 0.5rem;
}
.dark {
--background: 222.2 84% 4.9%;
--foreground: 210 40% 98%;
--muted: 217.2 32.6% 17.5%;
--muted-foreground: 215 20.2% 65.1%;
--popover: 222.2 84% 4.9%;
--popover-foreground: 210 40% 98%;
--card: 222.2 84% 4.9%;
--card-foreground: 210 40% 98%;
--border: 217.2 32.6% 17.5%;
--input: 217.2 32.6% 17.5%;
--primary: 210 40% 98%;
--primary-foreground: 222.2 47.4% 11.2%;
--secondary: 217.2 32.6% 17.5%;
--secondary-foreground: 210 40% 98%;
--accent: 217.2 32.6% 17.5%;
--accent-foreground: 210 40% 98%;
--destructive: 0 62.8% 30.6%;
--destructive-foreground: 210 40% 98%;
--ring: hsl(212.7,26.8%,83.9);
}
}
@layer base {
* {
@apply border-border;
}
body {
@apply bg-background text-foreground;
}
}

View file

@ -1,6 +0,0 @@
<script lang="ts">
export let text: string;
export let action: any = () => {};
</script>
<button class="btn px-4 py-2 mx-2 rounded-token variant-ringed-primary hover:variant-filled-primary active:variant-filled-primary" on:click={action}>{text}</button>

View file

@ -1,2 +0,0 @@
export const apiBaseUrl = 'https://wip.chellaris.net/api';
export const MACHINE_GROUP_ID = 12;

View file

@ -0,0 +1,19 @@
<script lang="ts">
import { Avatar as AvatarPrimitive } from "bits-ui";
import { cn } from "$lib/utils";
type $$Props = AvatarPrimitive.FallbackProps;
let className: $$Props["class"] = undefined;
export { className as class };
</script>
<AvatarPrimitive.Fallback
class={cn(
"flex h-full w-full items-center justify-center rounded-full bg-muted",
className
)}
{...$$restProps}
>
<slot />
</AvatarPrimitive.Fallback>

View file

@ -0,0 +1,18 @@
<script lang="ts">
import { Avatar as AvatarPrimitive } from "bits-ui";
import { cn } from "$lib/utils";
type $$Props = AvatarPrimitive.ImageProps;
let className: $$Props["class"] = undefined;
export let src: $$Props["src"] = undefined;
export let alt: $$Props["alt"] = undefined;
export { className as class };
</script>
<AvatarPrimitive.Image
{src}
{alt}
class={cn("aspect-square h-full w-full", className)}
{...$$restProps}
/>

View file

@ -0,0 +1,21 @@
<script lang="ts">
import { Avatar as AvatarPrimitive } from "bits-ui";
import { cn } from "$lib/utils";
type $$Props = AvatarPrimitive.Props;
let className: $$Props["class"] = undefined;
export let delayMs: $$Props["delayMs"] = undefined;
export { className as class };
</script>
<AvatarPrimitive.Root
{delayMs}
class={cn(
"relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full",
className
)}
{...$$restProps}
>
<slot />
</AvatarPrimitive.Root>

View file

@ -0,0 +1,13 @@
import Root from "./avatar.svelte";
import Image from "./avatar-image.svelte";
import Fallback from "./avatar-fallback.svelte";
export {
Root,
Image,
Fallback,
//
Root as Avatar,
Image as AvatarImage,
Fallback as AvatarFallback
};

View file

@ -0,0 +1,25 @@
<script lang="ts">
import { Button as ButtonPrimitive } from "bits-ui";
import { cn } from "$lib/utils";
import { buttonVariants, type Props, type Events } from ".";
type $$Props = Props;
type $$Events = Events;
let className: $$Props["class"] = undefined;
export let variant: $$Props["variant"] = "default";
export let size: $$Props["size"] = "default";
export let builders: $$Props["builders"] = [];
export { className as class };
</script>
<ButtonPrimitive.Root
{builders}
class={cn(buttonVariants({ variant, size, className }))}
type="button"
{...$$restProps}
on:click
on:keydown
>
<slot />
</ButtonPrimitive.Root>

View file

@ -0,0 +1,52 @@
import type { Button as ButtonPrimitive } from "bits-ui";
import { tv, type VariantProps } from "tailwind-variants";
import Root from "./button.svelte";
const buttonVariants = tv({
base: "inline-flex items-center justify-center rounded-md text-sm font-medium whitespace-nowrap transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50",
variants: {
variant: {
default:
"bg-primary text-primary-foreground shadow hover:bg-primary/90",
destructive:
"bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
outline:
"border border-input bg-transparent shadow-sm hover:bg-accent hover:text-accent-foreground",
secondary:
"bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
ghost: "hover:bg-accent hover:text-accent-foreground",
link: "text-primary underline-offset-4 hover:underline"
},
size: {
default: "h-9 px-4 py-2",
sm: "h-8 rounded-md px-3 text-xs",
lg: "h-10 rounded-md px-8",
icon: "h-9 w-9"
}
},
defaultVariants: {
variant: "default",
size: "default"
}
});
type Variant = VariantProps<typeof buttonVariants>["variant"];
type Size = VariantProps<typeof buttonVariants>["size"];
type Props = ButtonPrimitive.Props & {
variant?: Variant;
size?: Size;
};
type Events = ButtonPrimitive.Events;
export {
Root,
type Props,
type Events,
//
Root as Button,
type Props as ButtonProps,
type Events as ButtonEvents,
buttonVariants
};

View file

@ -0,0 +1,36 @@
<script lang="ts">
import { Checkbox as CheckboxPrimitive } from "bits-ui";
import { Check, Minus } from "radix-icons-svelte";
import { cn } from "$lib/utils";
type $$Props = CheckboxPrimitive.Props;
type $$Events = CheckboxPrimitive.Events;
let className: $$Props["class"] = undefined;
export let checked: $$Props["checked"] = false;
export { className as class };
</script>
<CheckboxPrimitive.Root
class={cn(
"box-content peer h-4 w-4 shrink-0 rounded-sm border border-primary shadow focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground data-[disabled=true]:cursor-not-allowed data-[disabled=true]:opacity-50",
className
)}
bind:checked
on:click
{...$$restProps}
>
<CheckboxPrimitive.Indicator
class={cn("flex items-center justify-center text-current h-4 w-4")}
let:isChecked
let:isIndeterminate
>
{#if isIndeterminate}
<Minus class="h-3.5 w-3.5" />
{:else}
<Check
class={cn("h-3.5 w-3.5", !isChecked && "text-transparent")}
/>
{/if}
</CheckboxPrimitive.Indicator>
</CheckboxPrimitive.Root>

View file

@ -0,0 +1,6 @@
import Root from "./checkbox.svelte";
export {
Root,
//
Root as Checkbox
};

View file

@ -0,0 +1,14 @@
<script lang="ts">
import { Collapsible as CollapsiblePrimitive } from "bits-ui";
import { slide } from "svelte/transition";
type $$Props = CollapsiblePrimitive.ContentProps;
export let transition: $$Props["transition"] = slide;
export let transitionConfig: $$Props["transitionConfig"] = {
duration: 150
};
</script>
<CollapsiblePrimitive.Content {transition} {transitionConfig} {...$$restProps}>
<slot />
</CollapsiblePrimitive.Content>

View file

@ -0,0 +1,15 @@
import { Collapsible as CollapsiblePrimitive } from "bits-ui";
import Content from "./collapsible-content.svelte";
const Root = CollapsiblePrimitive.Root;
const Trigger = CollapsiblePrimitive.Trigger;
export {
Root,
Content,
Trigger,
//
Root as Collapsible,
Content as CollapsibleContent,
Trigger as CollapsibleTrigger
};

View file

@ -0,0 +1,35 @@
<script lang="ts">
import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui";
import { cn } from "$lib/utils";
import { Check } from "radix-icons-svelte";
type $$Props = DropdownMenuPrimitive.CheckboxItemProps;
type $$Events = DropdownMenuPrimitive.CheckboxItemEvents;
let className: $$Props["class"] = undefined;
export let checked: $$Props["checked"] = undefined;
export { className as class };
</script>
<DropdownMenuPrimitive.CheckboxItem
bind:checked
class={cn(
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
className
)}
{...$$restProps}
on:click
on:keydown
on:focusin
on:focusout
on:pointerdown
on:pointerleave
on:pointermove
>
<span class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
<DropdownMenuPrimitive.CheckboxIndicator>
<Check class="h-4 w-4" />
</DropdownMenuPrimitive.CheckboxIndicator>
</span>
<slot />
</DropdownMenuPrimitive.CheckboxItem>

View file

@ -0,0 +1,26 @@
<script lang="ts">
import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui";
import { cn, flyAndScale } from "$lib/utils";
type $$Props = DropdownMenuPrimitive.ContentProps;
let className: $$Props["class"] = undefined;
export let sideOffset: $$Props["sideOffset"] = 4;
export let transition: $$Props["transition"] = flyAndScale;
export let transitionConfig: $$Props["transitionConfig"] = undefined;
export { className as class };
</script>
<DropdownMenuPrimitive.Content
{transition}
{transitionConfig}
{sideOffset}
class={cn(
"z-50 min-w-[8rem] rounded-md border bg-popover p-1 text-popover-foreground shadow-md focus:outline-none",
className
)}
{...$$restProps}
on:keydown
>
<slot />
</DropdownMenuPrimitive.Content>

View file

@ -0,0 +1,31 @@
<script lang="ts">
import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui";
import { cn } from "$lib/utils";
type $$Props = DropdownMenuPrimitive.ItemProps & {
inset?: boolean;
};
type $$Events = DropdownMenuPrimitive.ItemEvents;
let className: $$Props["class"] = undefined;
export let inset: $$Props["inset"] = undefined;
export { className as class };
</script>
<DropdownMenuPrimitive.Item
class={cn(
"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
inset && "pl-8",
className
)}
on:click
on:keydown
on:focusin
on:focusout
on:pointerdown
on:pointerleave
on:pointermove
{...$$restProps}
>
<slot />
</DropdownMenuPrimitive.Item>

View file

@ -0,0 +1,19 @@
<script lang="ts">
import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui";
import { cn } from "$lib/utils";
type $$Props = DropdownMenuPrimitive.LabelProps & {
inset?: boolean;
};
let className: $$Props["class"] = undefined;
export let inset: $$Props["inset"] = undefined;
export { className as class };
</script>
<DropdownMenuPrimitive.Label
class={cn("px-2 py-1.5 text-sm font-semibold", inset && "pl-8", className)}
{...$$restProps}
>
<slot />
</DropdownMenuPrimitive.Label>

View file

@ -0,0 +1,11 @@
<script lang="ts">
import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui";
type $$Props = DropdownMenuPrimitive.RadioGroupProps;
export let value: $$Props["value"] = undefined;
</script>
<DropdownMenuPrimitive.RadioGroup {...$$restProps} bind:value>
<slot />
</DropdownMenuPrimitive.RadioGroup>

View file

@ -0,0 +1,35 @@
<script lang="ts">
import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui";
import { cn } from "$lib/utils";
import { DotFilled } from "radix-icons-svelte";
type $$Props = DropdownMenuPrimitive.RadioItemProps;
type $$Events = DropdownMenuPrimitive.RadioItemEvents;
let className: $$Props["class"] = undefined;
export let value: DropdownMenuPrimitive.RadioItemProps["value"];
export { className as class };
</script>
<DropdownMenuPrimitive.RadioItem
class={cn(
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
className
)}
{value}
{...$$restProps}
on:click
on:keydown
on:focusin
on:focusout
on:pointerdown
on:pointerleave
on:pointermove
>
<span class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
<DropdownMenuPrimitive.RadioIndicator>
<DotFilled class="h-4 w-4 fill-current" />
</DropdownMenuPrimitive.RadioIndicator>
</span>
<slot />
</DropdownMenuPrimitive.RadioItem>

View file

@ -0,0 +1,14 @@
<script lang="ts">
import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui";
import { cn } from "$lib/utils";
type $$Props = DropdownMenuPrimitive.SeparatorProps;
let className: $$Props["class"] = undefined;
export { className as class };
</script>
<DropdownMenuPrimitive.Separator
class={cn("-mx-1 my-1 h-px bg-muted", className)}
{...$$restProps}
/>

View file

@ -0,0 +1,16 @@
<script lang="ts">
import { cn } from "$lib/utils";
import type { HTMLAttributes } from "svelte/elements";
type $$Props = HTMLAttributes<HTMLSpanElement>;
let className: $$Props["class"] = undefined;
export { className as class };
</script>
<span
class={cn("ml-auto text-xs tracking-widest opacity-60", className)}
{...$$restProps}
>
<slot />
</span>

View file

@ -0,0 +1,29 @@
<script lang="ts">
import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui";
import { cn, flyAndScale } from "$lib/utils";
type $$Props = DropdownMenuPrimitive.SubContentProps;
let className: $$Props["class"] = undefined;
export let transition: $$Props["transition"] = flyAndScale;
export let transitionConfig: $$Props["transitionConfig"] = {
x: -10,
y: 0
};
export { className as class };
</script>
<DropdownMenuPrimitive.SubContent
{transition}
{transitionConfig}
class={cn(
"z-50 min-w-[8rem] rounded-md border bg-popover p-1 text-popover-foreground shadow-lg focus:outline-none",
className
)}
{...$$restProps}
on:keydown
on:focusout
on:pointermove
>
<slot />
</DropdownMenuPrimitive.SubContent>

View file

@ -0,0 +1,32 @@
<script lang="ts">
import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui";
import { cn } from "$lib/utils";
import { ChevronRight } from "radix-icons-svelte";
type $$Props = DropdownMenuPrimitive.SubTriggerProps & {
inset?: boolean;
};
type $$Events = DropdownMenuPrimitive.SubTriggerEvents;
let className: $$Props["class"] = undefined;
export let inset: $$Props["inset"] = undefined;
export { className as class };
</script>
<DropdownMenuPrimitive.SubTrigger
class={cn(
"flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[highlighted]:bg-accent data-[state=open]:bg-accent",
inset && "pl-8",
className
)}
{...$$restProps}
on:click
on:keydown
on:focusin
on:focusout
on:pointerleave
on:pointermove
>
<slot />
<ChevronRight class="ml-auto h-4 w-4" />
</DropdownMenuPrimitive.SubTrigger>

View file

@ -0,0 +1,48 @@
import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui";
import Item from "./dropdown-menu-item.svelte";
import Label from "./dropdown-menu-label.svelte";
import Content from "./dropdown-menu-content.svelte";
import Shortcut from "./dropdown-menu-shortcut.svelte";
import RadioItem from "./dropdown-menu-radio-item.svelte";
import Separator from "./dropdown-menu-separator.svelte";
import RadioGroup from "./dropdown-menu-radio-group.svelte";
import SubContent from "./dropdown-menu-sub-content.svelte";
import SubTrigger from "./dropdown-menu-sub-trigger.svelte";
import CheckboxItem from "./dropdown-menu-checkbox-item.svelte";
const Sub = DropdownMenuPrimitive.Sub;
const Root = DropdownMenuPrimitive.Root;
const Trigger = DropdownMenuPrimitive.Trigger;
const Group = DropdownMenuPrimitive.Group;
export {
Sub,
Root,
Item,
Label,
Group,
Trigger,
Content,
Shortcut,
Separator,
RadioItem,
SubContent,
SubTrigger,
RadioGroup,
CheckboxItem,
//
Root as DropdownMenu,
Sub as DropdownMenuSub,
Item as DropdownMenuItem,
Label as DropdownMenuLabel,
Group as DropdownMenuGroup,
Content as DropdownMenuContent,
Trigger as DropdownMenuTrigger,
Shortcut as DropdownMenuShortcut,
RadioItem as DropdownMenuRadioItem,
Separator as DropdownMenuSeparator,
RadioGroup as DropdownMenuRadioGroup,
SubContent as DropdownMenuSubContent,
SubTrigger as DropdownMenuSubTrigger,
CheckboxItem as DropdownMenuCheckboxItem
};

View file

@ -0,0 +1,25 @@
import Root from "./input.svelte";
type FormInputEvent<T extends Event = Event> = T & {
currentTarget: EventTarget & HTMLInputElement;
};
export type InputEvents = {
blur: FormInputEvent<FocusEvent>;
change: FormInputEvent<Event>;
click: FormInputEvent<MouseEvent>;
focus: FormInputEvent<FocusEvent>;
keydown: FormInputEvent<KeyboardEvent>;
keypress: FormInputEvent<KeyboardEvent>;
keyup: FormInputEvent<KeyboardEvent>;
mouseover: FormInputEvent<MouseEvent>;
mouseenter: FormInputEvent<MouseEvent>;
mouseleave: FormInputEvent<MouseEvent>;
paste: FormInputEvent<ClipboardEvent>;
input: FormInputEvent<InputEvent>;
};
export {
Root,
//
Root as Input
};

View file

@ -0,0 +1,33 @@
<script lang="ts">
import type { HTMLInputAttributes } from "svelte/elements";
import { cn } from "$lib/utils";
import type { InputEvents } from ".";
type $$Props = HTMLInputAttributes;
type $$Events = InputEvents;
let className: $$Props["class"] = undefined;
export let value: $$Props["value"] = undefined;
export { className as class };
</script>
<input
class={cn(
"flex h-9 w-full rounded-md border border-input bg-background px-3 py-1 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-foreground file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50",
className
)}
bind:value
on:blur
on:change
on:click
on:focus
on:keydown
on:keypress
on:keyup
on:mouseover
on:mouseenter
on:mouseleave
on:paste
on:input
{...$$restProps}
/>

View file

@ -0,0 +1,7 @@
import Root from "./label.svelte";
export {
Root,
//
Root as Label
};

View file

@ -0,0 +1,19 @@
<script lang="ts">
import { Label as LabelPrimitive } from "bits-ui";
import { cn } from "$lib/utils";
type $$Props = LabelPrimitive.Props;
let className: $$Props["class"] = undefined;
export { className as class };
</script>
<LabelPrimitive.Root
class={cn(
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
className
)}
{...$$restProps}
>
<slot />
</LabelPrimitive.Root>

View file

@ -0,0 +1,52 @@
import { Menubar as MenubarPrimitive } from "bits-ui";
import Root from "./menubar.svelte";
import CheckboxItem from "./menubar-checkbox-item.svelte";
import Content from "./menubar-content.svelte";
import Item from "./menubar-item.svelte";
import Label from "./menubar-label.svelte";
import RadioItem from "./menubar-radio-item.svelte";
import Separator from "./menubar-separator.svelte";
import Shortcut from "./menubar-shortcut.svelte";
import SubContent from "./menubar-sub-content.svelte";
import SubTrigger from "./menubar-sub-trigger.svelte";
import Trigger from "./menubar-trigger.svelte";
const Menu = MenubarPrimitive.Menu;
const Group = MenubarPrimitive.Group;
const Sub = MenubarPrimitive.Sub;
const RadioGroup = MenubarPrimitive.RadioGroup;
export {
Root,
CheckboxItem,
Content,
Item,
Label,
RadioItem,
Separator,
Shortcut,
SubContent,
SubTrigger,
Trigger,
Menu,
Group,
Sub,
RadioGroup,
//
Root as Menubar,
CheckboxItem as MenubarCheckboxItem,
Content as MenubarContent,
Item as MenubarItem,
Label as MenubarLabel,
RadioItem as MenubarRadioItem,
Separator as MenubarSeparator,
Shortcut as MenubarShortcut,
SubContent as MenubarSubContent,
SubTrigger as MenubarSubTrigger,
Trigger as MenubarTrigger,
Menu as MenubarMenu,
Group as MenubarGroup,
Sub as MenubarSub,
RadioGroup as MenubarRadioGroup
};

View file

@ -0,0 +1,33 @@
<script lang="ts">
import { Menubar as MenubarPrimitive } from "bits-ui";
import { cn } from "$lib/utils";
import { Check } from "radix-icons-svelte";
type $$Props = MenubarPrimitive.CheckboxItemProps;
let className: $$Props["class"] = undefined;
export { className as class };
export let checked: $$Props["checked"] = undefined;
</script>
<MenubarPrimitive.CheckboxItem
bind:checked
class={cn(
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
className
)}
{...$$restProps}
on:click
on:keydown
on:focusin
on:focusout
on:pointerleave
on:pointermove
on:pointerdown
>
<span class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
<MenubarPrimitive.CheckboxIndicator>
<Check class="h-4 w-4" />
</MenubarPrimitive.CheckboxIndicator>
</span>
<slot />
</MenubarPrimitive.CheckboxItem>

View file

@ -0,0 +1,28 @@
<script lang="ts">
import { Menubar as MenubarPrimitive } from "bits-ui";
import { cn, flyAndScale } from "$lib/utils";
type $$Props = MenubarPrimitive.ContentProps;
let className: $$Props["class"] = undefined;
export let align: $$Props["align"] = "start";
export let alignOffset: $$Props["alignOffset"] = -4;
export let sideOffset: $$Props["sideOffset"] = 8;
export let transition: $$Props["transition"] = flyAndScale;
export let transitionConfig: $$Props["transitionConfig"] = undefined;
export { className as class };
</script>
<MenubarPrimitive.Content
{transition}
{transitionConfig}
{sideOffset}
{align}
{alignOffset}
class={cn(
"z-50 min-w-[12rem] rounded-md border bg-popover p-1 text-popover-foreground shadow-md focus:outline-none",
className
)}
{...$$restProps}
>
<slot />
</MenubarPrimitive.Content>

View file

@ -0,0 +1,31 @@
<script lang="ts">
import { Menubar as MenubarPrimitive } from "bits-ui";
import { cn } from "$lib/utils";
type $$Props = MenubarPrimitive.ItemProps & {
inset?: boolean;
};
type $$Events = MenubarPrimitive.ItemEvents;
let className: $$Props["class"] = undefined;
export let inset: $$Props["inset"] = undefined;
export { className as class };
</script>
<MenubarPrimitive.Item
class={cn(
"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
inset && "pl-8",
className
)}
{...$$restProps}
on:click
on:keydown
on:focusin
on:focusout
on:pointerleave
on:pointermove
on:pointerdown
>
<slot />
</MenubarPrimitive.Item>

View file

@ -0,0 +1,18 @@
<script lang="ts">
import { Menubar as MenubarPrimitive } from "bits-ui";
import { cn } from "$lib/utils";
type $$Props = MenubarPrimitive.LabelProps & {
inset?: boolean;
};
let className: $$Props["class"] = undefined;
export let inset: $$Props["inset"] = undefined;
export { className as class };
</script>
<MenubarPrimitive.Label
class={cn("px-2 py-1.5 text-sm font-semibold", inset && "pl-8", className)}
{...$$restProps}
>
<slot />
</MenubarPrimitive.Label>

View file

@ -0,0 +1,34 @@
<script lang="ts">
import { Menubar as MenubarPrimitive } from "bits-ui";
import { cn } from "$lib/utils";
import { DotFilled } from "radix-icons-svelte";
type $$Props = MenubarPrimitive.RadioItemProps;
type $$Events = MenubarPrimitive.RadioItemEvents;
let className: $$Props["class"] = undefined;
export let value: $$Props["value"];
export { className as class };
</script>
<MenubarPrimitive.RadioItem
{value}
class={cn(
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
className
)}
{...$$restProps}
on:click
on:keydown
on:focusin
on:focusout
on:pointerleave
on:pointermove
on:pointerdown
>
<span class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
<MenubarPrimitive.RadioIndicator>
<DotFilled class="h-4 w-4 fill-current" />
</MenubarPrimitive.RadioIndicator>
</span>
<slot />
</MenubarPrimitive.RadioItem>

View file

@ -0,0 +1,13 @@
<script lang="ts">
import { Menubar as MenubarPrimitive } from "bits-ui";
import { cn } from "$lib/utils";
type $$Props = MenubarPrimitive.SeparatorProps;
let className: $$Props["class"] = undefined;
export { className as class };
</script>
<MenubarPrimitive.Separator
class={cn("-mx-1 my-1 h-px bg-muted", className)}
{...$$restProps}
/>

View file

@ -0,0 +1,18 @@
<script lang="ts">
import { cn } from "$lib/utils";
import type { HTMLAttributes } from "svelte/elements";
type $$Props = HTMLAttributes<HTMLSpanElement>;
let className: $$Props["class"] = undefined;
export { className as class };
</script>
<span
class={cn(
"ml-auto text-xs tracking-widest text-muted-foreground",
className
)}
{...$$restProps}
>
<slot />
</span>

View file

@ -0,0 +1,25 @@
<script lang="ts">
import { Menubar as MenubarPrimitive } from "bits-ui";
import { cn, flyAndScale } from "$lib/utils";
type $$Props = MenubarPrimitive.SubContentProps;
let className: $$Props["class"] = undefined;
export let transition: $$Props["transition"] = flyAndScale;
export let transitionConfig: $$Props["transitionConfig"] = {
x: -10,
y: 0
};
export { className as class };
</script>
<MenubarPrimitive.SubContent
{transition}
{transitionConfig}
class={cn(
"z-50 min-w-[8rem] rounded-md border bg-popover p-1 text-popover-foreground shadow-lg focus:outline-none",
className
)}
{...$$restProps}
>
<slot />
</MenubarPrimitive.SubContent>

View file

@ -0,0 +1,32 @@
<script lang="ts">
import { Menubar as MenubarPrimitive } from "bits-ui";
import { cn } from "$lib/utils";
import { ChevronRight } from "radix-icons-svelte";
type $$Props = MenubarPrimitive.SubTriggerProps & {
inset?: boolean;
};
type $$Events = MenubarPrimitive.SubTriggerEvents;
let className: $$Props["class"] = undefined;
export let inset: $$Props["inset"] = undefined;
export { className as class };
</script>
<MenubarPrimitive.SubTrigger
class={cn(
"flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground",
inset && "pl-8",
className
)}
{...$$restProps}
on:click
on:keydown
on:focusin
on:focusout
on:pointerleave
on:pointermove
>
<slot />
<ChevronRight class="ml-auto h-4 w-4" />
</MenubarPrimitive.SubTrigger>

View file

@ -0,0 +1,23 @@
<script lang="ts">
import { Menubar as MenubarPrimitive } from "bits-ui";
import { cn } from "$lib/utils";
type $$Props = MenubarPrimitive.TriggerProps;
type $$Events = MenubarPrimitive.TriggerEvents;
let className: $$Props["class"] = undefined;
export { className as class };
</script>
<MenubarPrimitive.Trigger
class={cn(
"flex cursor-default select-none items-center rounded-sm px-3 py-1 text-sm font-medium outline-none focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground",
className
)}
{...$$restProps}
on:click
on:keydown
on:pointerenter
>
<slot />
</MenubarPrimitive.Trigger>

View file

@ -0,0 +1,18 @@
<script lang="ts">
import { Menubar as MenubarPrimitive } from "bits-ui";
import { cn } from "$lib/utils";
type $$Props = MenubarPrimitive.Props;
let className: $$Props["class"] = undefined;
export { className as class };
</script>
<MenubarPrimitive.Root
class={cn(
"flex h-9 items-center space-x-1 rounded-md border bg-background p-1 shadow-sm",
className
)}
>
<slot />
</MenubarPrimitive.Root>

View file

@ -0,0 +1,14 @@
import { Popover as PopoverPrimitive } from "bits-ui";
import Content from "./popover-content.svelte";
const Root = PopoverPrimitive.Root;
const Trigger = PopoverPrimitive.Trigger;
export {
Root,
Content,
Trigger,
//
Root as Popover,
Content as PopoverContent,
Trigger as PopoverTrigger
};

View file

@ -0,0 +1,27 @@
<script lang="ts">
import { Popover as PopoverPrimitive } from "bits-ui";
import { cn, flyAndScale } from "$lib/utils";
type $$Props = PopoverPrimitive.ContentProps;
let className: $$Props["class"] = undefined;
export let transition: $$Props["transition"] = flyAndScale;
export let transitionConfig: $$Props["transitionConfig"] = undefined;
export let align: $$Props["align"] = "center";
export let sideOffset: $$Props["sideOffset"] = 4;
export { className as class };
</script>
<PopoverPrimitive.Content
{transition}
{transitionConfig}
{align}
{sideOffset}
{...$$restProps}
class={cn(
"z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none",
className
)}
>
<slot />
</PopoverPrimitive.Content>

View file

@ -0,0 +1,34 @@
import { Select as SelectPrimitive } from "bits-ui";
import Label from "./select-label.svelte";
import Item from "./select-item.svelte";
import Content from "./select-content.svelte";
import Trigger from "./select-trigger.svelte";
import Separator from "./select-separator.svelte";
const Root = SelectPrimitive.Root;
const Group = SelectPrimitive.Group;
const Input = SelectPrimitive.Input;
const Value = SelectPrimitive.Value;
export {
Root,
Item,
Group,
Input,
Label,
Value,
Content,
Trigger,
Separator,
//
Root as Select,
Item as SelectItem,
Group as SelectGroup,
Input as SelectInput,
Label as SelectLabel,
Value as SelectValue,
Content as SelectContent,
Trigger as SelectTrigger,
Separator as SelectSeparator
};

View file

@ -0,0 +1,36 @@
<script lang="ts">
import { Select as SelectPrimitive } from "bits-ui";
import { cn, flyAndScale } from "$lib/utils";
import { scale } from "svelte/transition";
type $$Props = SelectPrimitive.ContentProps;
let className: $$Props["class"] = undefined;
export let sideOffset: $$Props["sideOffset"] = 4;
export let inTransition: $$Props["inTransition"] = flyAndScale;
export let inTransitionConfig: $$Props["inTransitionConfig"] = undefined;
export let outTransition: $$Props["outTransition"] = scale;
export let outTransitionConfig: $$Props["outTransitionConfig"] = {
start: 0.95,
opacity: 0,
duration: 50
};
export { className as class };
</script>
<SelectPrimitive.Content
{inTransition}
{inTransitionConfig}
{outTransition}
{outTransitionConfig}
{sideOffset}
class={cn(
"relative z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md focus:outline-none",
className
)}
{...$$restProps}
>
<div class="w-full p-1">
<slot />
</div>
</SelectPrimitive.Content>

View file

@ -0,0 +1,35 @@
<script lang="ts">
import { cn } from "$lib/utils";
import { Select as SelectPrimitive } from "bits-ui";
import { Check } from "radix-icons-svelte";
type $$Props = SelectPrimitive.ItemProps;
type $$Events = Required<SelectPrimitive.ItemEvents>;
let className: $$Props["class"] = undefined;
export let value: $$Props["value"];
export let label: $$Props["label"] = undefined;
export let disabled: $$Props["disabled"] = undefined;
export { className as class };
</script>
<SelectPrimitive.Item
{value}
{disabled}
{label}
class={cn(
"relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-2 pr-8 text-sm outline-none data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
className
)}
{...$$restProps}
on:click
on:pointermove
on:focusin
>
<span class="absolute right-2 flex h-3.5 w-3.5 items-center justify-center">
<SelectPrimitive.ItemIndicator>
<Check class="h-4 w-4" />
</SelectPrimitive.ItemIndicator>
</span>
<slot />
</SelectPrimitive.Item>

View file

@ -0,0 +1,16 @@
<script lang="ts">
import { Select as SelectPrimitive } from "bits-ui";
import { cn } from "$lib/utils";
type $$Props = SelectPrimitive.LabelProps;
let className: $$Props["class"] = undefined;
export { className as class };
</script>
<SelectPrimitive.Label
class={cn("px-2 py-1.5 text-sm font-semibold", className)}
{...$$restProps}
>
<slot />
</SelectPrimitive.Label>

View file

@ -0,0 +1,14 @@
<script lang="ts">
import { Select as SelectPrimitive } from "bits-ui";
import { cn } from "$lib/utils";
type $$Props = SelectPrimitive.SeparatorProps;
let className: $$Props["class"] = undefined;
export { className as class };
</script>
<SelectPrimitive.Separator
class={cn("-mx-1 my-1 h-px bg-muted", className)}
{...$$restProps}
/>

View file

@ -0,0 +1,24 @@
<script lang="ts">
import { Select as SelectPrimitive } from "bits-ui";
import { CaretSort } from "radix-icons-svelte";
import { cn } from "$lib/utils";
type $$Props = SelectPrimitive.TriggerProps;
type $$Events = SelectPrimitive.TriggerEvents;
let className: $$Props["class"] = undefined;
export { className as class };
</script>
<SelectPrimitive.Trigger
class={cn(
"flex h-9 w-full items-center justify-between rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50",
className
)}
{...$$restProps}
>
<slot />
<div>
<CaretSort class="h-4 w-4 opacity-50" />
</div>
</SelectPrimitive.Trigger>

View file

@ -0,0 +1,7 @@
import Root from "./separator.svelte";
export {
Root,
//
Root as Separator
};

View file

@ -0,0 +1,22 @@
<script lang="ts">
import { Separator as SeparatorPrimitive } from "bits-ui";
import { cn } from "$lib/utils";
type $$Props = SeparatorPrimitive.Props;
let className: $$Props["class"] = undefined;
export let orientation: $$Props["orientation"] = "horizontal";
export let decorative: $$Props["decorative"] = undefined;
export { className as class };
</script>
<SeparatorPrimitive.Root
class={cn(
"shrink-0 bg-border",
orientation === "horizontal" ? "h-[1px] w-full" : "h-full w-[1px]",
className
)}
{orientation}
{decorative}
{...$$restProps}
/>

View file

@ -0,0 +1,7 @@
import Root from "./skeleton.svelte";
export {
Root,
//
Root as Skeleton
};

View file

@ -0,0 +1,14 @@
<script lang="ts">
import { cn } from "$lib/utils";
import type { HTMLAttributes } from "svelte/elements";
type $$Props = HTMLAttributes<HTMLDivElement>;
let className: $$Props["class"] = undefined;
export { className as class };
</script>
<div
class={cn("animate-pulse rounded-md bg-primary/10", className)}
{...$$restProps}
/>

View file

@ -0,0 +1,12 @@
<svelte:options runes={true} />
<script lang="ts">
import UserSettingsStore from '$lib/stores/UserSettingsStore';
import { themes } from '$lib/types/themes';
let {children, onclick} = $props()
let themeId = $derived($UserSettingsStore.themeId)
</script>
<button {onclick} class="border-2 border-{themes[themeId].primaryAction} hover:border-{themes[themeId].secondaryAction}">{@render children()}</button>

View file

@ -0,0 +1,2 @@
export const apiBaseUrl = 'https://www.chellaris.net/api';
export const MACHINE_GROUP_ID = 12;

View file

@ -0,0 +1,6 @@
import { writable, type Writable } from "svelte/store";
import { UserSettings } from '$lib/types/userSettings';
const UserSettingsStore: Writable<UserSettings> = writable(new UserSettings());
export default UserSettingsStore;

27
src/lib/types/themes.ts Normal file
View file

@ -0,0 +1,27 @@
export type Theme = {
backgroundColor: string,
textColor: string,
primaryAction: string,
secondaryAction: string,
}
const lightTheme = {
backgroundColor: "slate-50",
textColor: "slate-950",
primaryAction: "sky-300",
secondaryAction: "sky-600",
}
const darkTheme = {
backgroundColor: "slate-950",
textColor: "slate-50",
primaryAction: "sky-300",
secondaryAction: "sky-600",
}
export const themes = [
lightTheme,
darkTheme
]

View file

@ -0,0 +1,8 @@
export class UserSettings {
themeId: number
constructor() {
this.themeId = 1 // DEBUG
return this
}
}

62
src/lib/utils.ts Normal file
View file

@ -0,0 +1,62 @@
import { type ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";
import { cubicOut } from "svelte/easing";
import type { TransitionConfig } from "svelte/transition";
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
type FlyAndScaleParams = {
y?: number;
x?: number;
start?: number;
duration?: number;
};
export const flyAndScale = (
node: Element,
params: FlyAndScaleParams = { y: -8, x: 0, start: 0.95, duration: 150 }
): TransitionConfig => {
const style = getComputedStyle(node);
const transform = style.transform === "none" ? "" : style.transform;
const scaleConversion = (
valueA: number,
scaleA: [number, number],
scaleB: [number, number]
) => {
const [minA, maxA] = scaleA;
const [minB, maxB] = scaleB;
const percentage = (valueA - minA) / (maxA - minA);
const valueB = percentage * (maxB - minB) + minB;
return valueB;
};
const styleToString = (
style: Record<string, number | string | undefined>
): string => {
return Object.keys(style).reduce((str, key) => {
if (style[key] === undefined) return str;
return str + `${key}:${style[key]};`;
}, "");
};
return {
duration: params.duration ?? 200,
delay: 0,
css: (t) => {
const y = scaleConversion(t, [0, 1], [params.y ?? 5, 0]);
const x = scaleConversion(t, [0, 1], [params.x ?? 0, 0]);
const scale = scaleConversion(t, [0, 1], [params.start ?? 0.95, 1]);
return styleToString({
transform: `${transform} translate3d(${x}px, ${y}px, 0) scale(${scale})`,
opacity: t
});
},
easing: cubicOut
};
};

View file

@ -1,18 +1,24 @@
<svelte:options runes={true} />
<script lang="ts"> <script lang="ts">
import '../app.postcss'; import '../app.postcss';
import { browser } from '$app/environment';
import { Modal, autoModeWatcher, type ModalComponent, getModalStore, type ModalSettings, initializeStores } from '@skeletonlabs/skeleton'; import { writable } from 'svelte/store';
import { apiBaseUrl } from '$lib/components/consts'; import { themes } from '$lib/types/themes'
import { apiBaseUrl } from '$lib/components_custom/consts';
import { LeanChellarisDataStore } from '$lib/stores/ChellarisData'; import { LeanChellarisDataStore } from '$lib/stores/ChellarisData';
import Header from './Header.svelte'; import Header from './Header.svelte';
import { AppShell } from '@skeletonlabs/skeleton';
import Settings from './Settings.svelte'; import Settings from './Settings.svelte';
import AdminSelectedGameStore from '$lib/stores/admin-page/GameStore'; import AdminSelectedGameStore from '$lib/stores/admin-page/GameStore';
import type { ChellarisGameInfo } from '$lib/types/chellaris'; import type { ChellarisGameInfo } from '$lib/types/chellaris';
import UserSettingsStore from '$lib/stores/UserSettingsStore';
import AuthTokenStore from '$lib/stores/AuthTokenStore';
initializeStores(); if (browser) {
$AuthTokenStore = document.cookie.split("=")[document.cookie.split("=").length - 1];
}
$: { /* $: {
fetch(apiBaseUrl + '/v3/ethics').then((res) => { fetch(apiBaseUrl + '/v3/ethics').then((res) => {
res.json().then((data) => { res.json().then((data) => {
$LeanChellarisDataStore.ethics = data.ethics; $LeanChellarisDataStore.ethics = data.ethics;
@ -33,33 +39,16 @@
}); });
}); });
} }*/
const modalComponentRegistry: Record<string, ModalComponent> = {
settingsModal: {
ref: Settings,
props: { background: 'bg-red-500'}
}
}
</script> </script>
<svelte:head>{@html `<script>${autoModeWatcher.toString()} autoModeWatcher();</script>`}</svelte:head> <Header />
<div class="m-4 border-2 border-accent rounded-lg h-[calc(100%-5.75rem-1px)]">
<Modal components={modalComponentRegistry}/>
<AppShell regionPage="relative">
<svelte:fragment slot="header">
<Header/>
</svelte:fragment>
<!-- (sidebarLeft) -->
<!-- (sidebarRight) -->
<!-- <svelte:fragment slot="pageHeader">Page Header</svelte:fragment> -->
<!-- Router Slot -->
<slot /> <slot />
<!-- ---- / ---- --> </div>
<!-- (pageFooter) --> <!-- Router Slot -->
<!-- (footer) --> <!-- ---- / ---- -->
</AppShell> <!-- (pageFooter) -->
<!-- (footer) -->

View file

@ -1,13 +1,22 @@
<!-- YOU CAN DELETE EVERYTHING IN THIS PAGE --> <svelte:options runes={true} />
<div class="container h-full mx-auto flex justify-center items-center"> <script lang="ts">
</script>
<svelte:head>
<title>Home</title>
<meta name="description" content="Landing Page for the Chellaris Multiplayer Group" />
</svelte:head>
<div class="container py-2 h-full mx-auto flex flex-col justify-center items-center">
<h1>Chellaris</h1>
<div class="space-y-5"> <div class="space-y-5">
<ul> <ul>
<li> <li>
<a class="hover:text-primary-500" href="/graphs">Graphs</a> <a class="hover:text-primary-500" href="/current">Current Game</a>
</li> </li>
<li> <li>
<a class="hover:text-primary-500" href="/legacy-graphs">Game 15 Graphs</a> <a class="hover:text-primary-500" href="/archive/1">Previous Game</a>
</li> </li>
</ul> </ul>
</div> </div>

View file

@ -1,55 +1,194 @@
<svelte:options runes={true} />
<script lang="ts"> <script lang="ts">
import { page } from '$app/stores'; import { page } from '$app/stores';
import discord from '$lib/images/discord.svg'; import discord from '$lib/images/discord.svg';
import { AppBar, LightSwitch, TabAnchor, TabGroup, getModalStore, type ModalSettings } from '@skeletonlabs/skeleton'; import AuthTokenStore from '$lib/stores/AuthTokenStore';
import Settings from './Settings.svelte'; import { Exit, EyeClosed, EyeOpen } from 'radix-icons-svelte';
import Button from '$lib/components/Button.svelte'; import { Button } from '$lib/components/ui/button';
import { Separator } from '$lib/components/ui/separator';
import { Input } from '$lib/components/ui/input';
import * as Popover from '$lib/components/ui/popover';
import * as Avatar from '$lib/components/ui/avatar';
let showSettings = false; type User = {
token: string,
const modalStore = getModalStore(); tokenVisible: boolean,
discord: string | undefined,
const openSettings = () => { picture: string | undefined,
const settings: ModalSettings = { admin: boolean
type: 'component', }
component: 'settingsModal'
}; let user: User | undefined = $state(undefined);
modalStore.trigger(settings);
function handleLogout() {
$AuthTokenStore = ""
document.cookie = "authToken=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/"
user = undefined
}
async function handleLogin() {
// pretend we are handling the login via a fetch
await new Promise((resolve) => setTimeout(resolve, 200));
let ret = {
token: $AuthTokenStore,
discord: "neshura",
picture: "/avatar.png",
admin: true
}
if (true) {
const d = new Date();
let authExpiryDays = 14
d.setTime(d.getTime() + (authExpiryDays*24*60*60*1000));
document.cookie = "authToken=" + ret.token + "; expires=" + d.toUTCString() + "; path=/";
user = {
token: ret.token,
tokenVisible: false,
discord: ret.discord ?? undefined,
picture: ret.picture ?? undefined,
admin: ret.admin
}
}
else {
// handle login failure
}
}
if ($AuthTokenStore != "") {
handleLogin()
} }
</script> </script>
<AppBar gridColumns="grid-cols-3" slotDefault="place-self-center" slotTrail="place-content-end"> <div class="flex gap-2 items-center justify-center py-3 bg-primary-foreground">
<svelte:fragment slot="lead"> <!-- spacer -->
<p /> <Button
</svelte:fragment> variant="link"
<TabGroup href="https://discord.gg/invite/BYNeHaPNh9"
padding="px-4 py-2" target="_blank"
justify="justify-center" rel="noopener noreferrer"
rounded="rounded-tr-xl rounded-tl-xl" class="h-8 w-8 items-center flex"
border="border-b-2 border-primary-500" >
active="variant-glass-primary hover:variant-ghost-primary" <Avatar.Root class="h-6 w-6">
hover="hover:variant-ghost-primary" <Avatar.Image src={discord} alt="DC" />
> <Avatar.Fallback>US</Avatar.Fallback>
<!--<div aria-current={$page.url.pathname.startsWith('/sign-up') ? 'page' : undefined}> </Avatar.Root>
<a href="/sign-up">Empire Sign-Up</a> </Button>
</div>--> <Button
<TabAnchor class="mr-1" href="/" selected={$page.url.pathname === '/'}>Home</TabAnchor> variant="ghost"
<TabAnchor class="mx-1" href="/graphs" selected={$page.url.pathname.startsWith('/graphs')}>Graphs</TabAnchor> href="/"
<TabAnchor class="mx-1" href="/legacy-graphs" selected={$page.url.pathname === '/legacy-graphs'}>Game 15 Graphs</TabAnchor> class="{$page.url.pathname === '/' ? 'text-accent-foreground bg-accent' : ''}"
<TabAnchor class="mx-1" href="/admin" selected={$page.url.pathname === '/admin'}>Admin Menu</TabAnchor> >
<TabAnchor class="ml-1" href="/about" selected={$page.url.pathname === '/about'}>About</TabAnchor> Home
</TabGroup> </Button>
<Button
<svelte:fragment slot="trail"> variant="ghost"
<Button text="Settings" action={openSettings} /> href="/current"
<LightSwitch /> class="{$page.url.pathname === '/current' ? 'text-accent-foreground bg-accent' : ''}"
<div> >
<!-- Logo --> Game [Current Game]
<a class="lg:!ml-0 w-[32px] lg:w-auto overflow-hidden" href="https://discord.gg/invite/BYNeHaPNh9" target="_blank" rel="noopener noreferrer"> </Button>
<img width="32px" height="32px" src={discord} alt="Discord" /> <Button
</a> variant="ghost"
</div> href="/archive"
</svelte:fragment> class="{$page.url.pathname.startsWith('/archive') ? 'text-accent-foreground bg-accent' : ''}"
</AppBar> >
<div /> Archives
</Button>
{#if user !== undefined && user.admin}
<Button
variant="ghost"
href="/admin"
class="{$page.url.pathname === '/admin' ? 'text-accent-foreground bg-accent' : ''}"
>
Admin
</Button>
<Button
variant="ghost"
href="/admin/new"
class="{$page.url.pathname === '/admin/new' ? 'text-accent-foreground bg-accent' : ''}"
>
Admin (New)
</Button>
{/if}
{#if user !== undefined}
<Popover.Root>
<Popover.Trigger>
<Button
variant="ghost"
class="relative h-8 w-8 rounded-full align-middle"
>
<Avatar.Root class="h-8 w-8">
<Avatar.Image src={user.picture} alt="@user" />
<Avatar.Fallback>US</Avatar.Fallback>
</Avatar.Root>
</Button>
</Popover.Trigger>
<Popover.Content class="w-56 p-0">
<div class="w-full p-1">
<div class="px-2 py-1.5 font-normal">
<div class="flex flex-row items-center gap-1">
{#if user.tokenVisible}
<p class="text-sm font-medium leading-none w-12">{user.token}</p> <!--Make the UserToken hidden by default-->
{:else}
<p class="text-sm font-medium leading-none w-12">******</p>
{/if}
<Button variant="ghost" size="sm" class="p-0 h-2 justify-start" on:click={() => {if (user !== undefined) user.tokenVisible = !user.tokenVisible}}>
{#if user.tokenVisible}
<EyeOpen />
{:else}
<EyeClosed />
{/if}
</Button>
</div>
{#if user.discord}
<p class="mt-1 text-xs leading-none text-muted-foreground">@{user.discord}</p>
{/if}
</div>
</div>
<Separator orientation="horizontal" />
<div class="w-full p-1">
<Button variant="ghost" class="justify-start h-8 px-2 py-1.5 text-sm w-full" href="/profile">
Profile
</Button>
<Button variant="ghost" class="justify-start h-8 px-2 py-1.5 text-sm w-full" href="/settings">
Settings
</Button>
</div>
<Separator orientation="horizontal" />
<div class="w-full p-1">
<Button variant="ghost" class="flex flex-row items-center justify-start h-8 space-x-1 px-2 py-1.5 text-sm w-full" on:click={handleLogout}>
<p>Log out </p>
<Exit />
</Button>
</div>
</Popover.Content>
</Popover.Root>
{:else}
<Popover.Root>
<Popover.Trigger>
<Button
variant="ghost"
class="relative"
>
Log In
</Button>
</Popover.Trigger>
<Popover.Content class="w-40 p-1 ">
<form class="flex flex-col items-center gap-1" on:submit={handleLogin}>
<Input type="text" placeholder="User Token" bind:value={$AuthTokenStore} />
<div class="flex flex-row">
<Button variant="link" class="flex flex-row items-center h-8 space-x-1 px-2 py-1.5 text-sm w-full text-muted-foreground" href="/sign-up" >
Sign Up
</Button>
<Button variant="secondary" class="justify-start h-8 px-2 py-1.5 text-sm w-full" type="submit">
Log In
</Button>
</div>
</form>
</Popover.Content>
</Popover.Root>
{/if}
</div>
<Separator orientation="horizontal"></Separator>

View file

@ -1,12 +1,9 @@
<script lang="ts"> <script lang="ts">
import { browser } from "$app/environment"; import { browser } from "$app/environment";
import Button from "$lib/components/Button.svelte"; import Button from "$lib/components_custom/Button.svelte";
import AuthTokenStore from "$lib/stores/AuthTokenStore"; import AuthTokenStore from "$lib/stores/AuthTokenStore";
import { getModalStore } from "@skeletonlabs/skeleton";
import { fade, slide } from "svelte/transition"; import { fade, slide } from "svelte/transition";
const modalStore = getModalStore();
let showAuthSaved = false; let showAuthSaved = false;
let showPlaceholder = true; let showPlaceholder = true;
@ -32,7 +29,7 @@
const handleSubmit = () => { const handleSubmit = () => {
console.log("submitted ", gameGroupSettings); console.log("submitted ", gameGroupSettings);
document.cookie = "authToken=" + authToken; document.cookie = "authToken=" + authToken;
modalStore.close(); // somehow close the modal
} }
let gameGroupSettings: [] = []; let gameGroupSettings: [] = [];

View file

@ -1,4 +1,4 @@
import { apiBaseUrl } from "$lib/components/consts"; import { apiBaseUrl } from "$lib/components_custom/consts";
import { redirect } from "@sveltejs/kit"; import { redirect } from "@sveltejs/kit";
export async function load({ cookies }) { export async function load({ cookies }) {
@ -9,7 +9,8 @@ export async function load({ cookies }) {
} }
})).json(); })).json();
if (!auth.admin && !auth.moderator) { /*if (!auth.admin && !auth.moderator) {
throw redirect(303, '/401'); throw redirect(303, '/401');
} }*/
console.log("WARNING: Admin Auth Check is disabled! DO NOT USE IN PRODUCTION")
} }

View file

@ -1,7 +1,7 @@
<script lang="ts"> <script lang="ts">
import { browser } from '$app/environment'; import { browser } from '$app/environment';
import LoadingSpinnerLocal from '$lib/components/LoadingSpinnerLocal.svelte'; import LoadingSpinnerLocal from '$lib/components_custom/LoadingSpinnerLocal.svelte';
import { apiBaseUrl } from '$lib/components/consts'; import { apiBaseUrl } from '$lib/components_custom/consts';
import AuthTokenStore from '$lib/stores/AuthTokenStore'; import AuthTokenStore from '$lib/stores/AuthTokenStore';
import AdminSelectedEmpireStore from '$lib/stores/admin-page/EmpireStore'; import AdminSelectedEmpireStore from '$lib/stores/admin-page/EmpireStore';
import AdminSelectedGameStore from '$lib/stores/admin-page/GameStore'; import AdminSelectedGameStore from '$lib/stores/admin-page/GameStore';

View file

@ -1,4 +1,4 @@
import { apiBaseUrl } from "$lib/components/consts"; import { apiBaseUrl } from "$lib/components_custom/consts";
import AdminSelectedEmpireStore from "$lib/stores/admin-page/EmpireStore"; import AdminSelectedEmpireStore from "$lib/stores/admin-page/EmpireStore";
import AdminSelectedGameStore from "$lib/stores/admin-page/GameStore"; import AdminSelectedGameStore from "$lib/stores/admin-page/GameStore";
import type { ChellarisGameInfo } from "$lib/types/chellaris"; import type { ChellarisGameInfo } from "$lib/types/chellaris";

View file

@ -1,7 +1,7 @@
<script lang="ts"> <script lang="ts">
import DropDown from '$lib/components/DropDown.svelte'; import DropDown from '$lib/components_custom/DropDown.svelte';
import DropDownElement from '$lib/components/DropDownElement.svelte'; import DropDownElement from '$lib/components_custom/DropDownElement.svelte';
import { MACHINE_GROUP_ID, apiBaseUrl } from '$lib/components/consts'; import { MACHINE_GROUP_ID, apiBaseUrl } from '$lib/components_custom/consts';
import { LeanChellarisDataStore } from '$lib/stores/ChellarisData'; import { LeanChellarisDataStore } from '$lib/stores/ChellarisData';
import type { ChellarisEmpire, ChellarisGameGroup } from '$lib/types/chellaris'; import type { ChellarisEmpire, ChellarisGameGroup } from '$lib/types/chellaris';
import type { EmpireEthic } from '$lib/types/stellaris'; import type { EmpireEthic } from '$lib/types/stellaris';

View file

@ -0,0 +1,16 @@
import { apiBaseUrl } from "$lib/components_custom/consts";
import { redirect } from "@sveltejs/kit";
export async function load({ cookies }) {
const auth = await (await fetch(apiBaseUrl + "/v3/auth", {
headers: {
'Content-Type': 'application/json',
'x-api-key': cookies.get("authToken") || ""
}
})).json();
/*if (!auth.admin && !auth.moderator) {
throw redirect(303, '/401');
}*/
console.log("WARNING: Admin Auth Check is disabled! DO NOT USE IN PRODUCTION")
}

View file

@ -0,0 +1,372 @@
<svelte:options runes={true} />
<script lang="ts">
import { Separator } from '$lib/components/ui/separator';
import * as Collapsible from "$lib/components/ui/collapsible";
import { Button } from '$lib/components/ui/button';
import { CaretDown, CaretRight } from 'radix-icons-svelte';
import { Checkbox } from '$lib/components/ui/checkbox';
import { Label } from '$lib/components/ui/label';
import { Skeleton } from '$lib/components/ui/skeleton';
import { range } from 'svelte-loading-spinners/utils';
import * as Select from '$lib/components/ui/select';
import AdminSelectedGameStore from '$lib/stores/admin-page/GameStore';
import AdminSelectedGroupStore from '$lib/stores/admin-page/GroupStore';
import AdminSelectedEmpireStore from '$lib/stores/admin-page/EmpireStore';
import {
type ChellarisEmpire,
type ChellarisGameGroup,
type ChellarisGameInfo,
createBlankEmpire
} from '$lib/types/chellaris';
import { apiBaseUrl } from '$lib/components_custom/consts';
// Dummy Data, Replace with API fetch
function defaultEmpires() {
return [
{
id: 0,
name: "Test Empire 1"
},
{
id: 1,
name: "Test Empire 2"
}
]
}
function defaultGroups() {
return [
{
id: 0,
checked: false,
name: "Test Group 1",
empires: defaultEmpires()
},
{
id: 1,
checked: false,
name: "Test Group 2",
empires: defaultEmpires()
}
]
}
type Empire = {
id: number,
name: string
}
type Group = {
id: number,
checked: boolean,
name: string,
empires: Array<Empire>
}
type Game = {
id: number,
checked: boolean,
name: string,
groups: Array<Group>
}
let games: Array<Game> = $state([
{
id: 0,
checked: false,
name: "Test 1",
groups: defaultGroups()
},
{
id: 1,
checked: false,
name: "Test 2",
groups: defaultGroups()
},
{
id: 2,
checked: false,
name: "Test 3",
groups: defaultGroups()
},
{
id: 3,
checked: false,
name: "Test 4",
groups: defaultGroups()
}
])
let filteredGames = $derived(games.filter(game => game.checked))
let filteredGroups = $derived(filteredGames.flatMap(game => game.groups.filter(group => group.checked)))
$effect(() => {
console.log(games)
})
const modes = [
{ value: "Game", label: "Games" },
{ value: "Group", label: "Groups" },
{ value: "Empire", label: "Empires" },
{ value: "Ethic", label: "Ethics" },
{ value: "Portrait", label: "Portraits" },
{ value: "User", label: "Users" }
];
let modeSelect = $state(modes[0]);
// Helper Function
function newGame() {
let newGame = {
id: games.length,
checked: false,
name: `Test ${games.length + 1}`,
groups: defaultGroups()
}
return newGame
}
</script>
<div class="h-full grid grid-cols-5">
<div class="pt-4 px-2 flex flex-col items-center border-r-2 border-accent gap-2">
<div class="flex flex-row gap-2">
<h2 class="self-center">
Editing
</h2>
<Select.Root bind:selected={modeSelect}>
<Select.Trigger class="w-28">
<Select.Value />
</Select.Trigger>
<Select.Content>
<Select.Group>
{#each modes as mode}
<Select.Item value={mode.value} label={mode.label}>
{mode.label}
</Select.Item>
{/each}
</Select.Group>
</Select.Content>
<Select.Input name="modeSelect" />
</Select.Root>
</div>
<Separator orientation="horizontal" class="w-[90%]" />
<h2>{modeSelect.value} Filters</h2>
{#if modeSelect.value == modes[0].value}
{:else if modeSelect.value == modes[1].value}
<Separator orientation="horizontal" class="w-[90%]" />
<Collapsible.Root class="w-[90%] space-y-2">
<Collapsible.Trigger asChild let:builder>
<Button builders={[builder]} variant="ghost" size="sm" class="w-full p-0 justify-start">
{#if builder["data-state"] == "open"}
<CaretDown />
{:else}
<CaretRight />
{/if}
<h4 class="rounded-md text-sm font-semibold text-left align-middle">
Games
</h4>
</Button>
</Collapsible.Trigger>
<Collapsible.Content class="space-y-2">
{#each games as game}
<div class="pl-4 flex items-center space-x-2">
<Checkbox id="{game.id.toString()}" checked={game.checked} on:click={() => game.checked = !game.checked} />
<Label
for="{game.id.toString()}"
class=""
>
{game.name}
</Label>
</div>
{/each}
</Collapsible.Content>
</Collapsible.Root>
<Separator orientation="horizontal" class="w-[90%]" />
{:else if modeSelect.value == modes[2].value}
<Separator orientation="horizontal" class="w-[90%]" />
<Collapsible.Root class="w-[90%] space-y-2">
<Collapsible.Trigger asChild let:builder>
<Button builders={[builder]} variant="ghost" size="sm" class="w-full p-0 justify-start">
{#if builder["data-state"] == "open"}
<CaretDown />
{:else}
<CaretRight />
{/if}
<h4 class="rounded-md text-sm font-semibold text-left align-middle">
Games
</h4>
</Button>
</Collapsible.Trigger>
<Collapsible.Content class="space-y-2">
{#each games as game}
<div class="pl-4 flex items-center space-x-2">
<Checkbox id="{game.id.toString()}" checked={game.checked} on:click={() => game.checked = !game.checked} />
<Label
for="{game.id.toString()}"
class=""
>
{game.name}
</Label>
</div>
{/each}
</Collapsible.Content>
</Collapsible.Root>
<Collapsible.Root class="w-[90%] space-y-2">
<Collapsible.Trigger asChild let:builder>
<Button builders={[builder]} variant="ghost" size="sm" class="w-full p-0 justify-start">
{#if builder["data-state"] == "open"}
<CaretDown />
{:else}
<CaretRight />
{/if}
<h4 class="rounded-md text-sm font-semibold text-left align-middle">
Groups
</h4>
</Button>
</Collapsible.Trigger>
<Collapsible.Content class="space-y-2">
{#if games.filter(value => value.checked == true).length == 0}
<div class="pl-4 flex flex-col items-center space-x-2 text-muted-foreground">
<Label class="text-center" >No Games Selected</Label>
</div>
{/if}
{#each games as game}
{#if game.checked}
{#each game.groups as group}
<div class="pl-4 flex items-center space-x-2">
<Checkbox id="{game.id}-{group.id}" checked={group.checked} on:click={() => {games[game.id].groups[group.id].checked = !group.checked}} />
<Label
for="{game.id}-{group.id}"
class=""
>
{game.name} {group.name}
</Label>
</div>
{/each}
{/if}
{/each}
</Collapsible.Content>
</Collapsible.Root>
<Separator orientation="horizontal" class="w-[90%]" />
{:else if modeSelect.value == modes[3].value}
<Separator orientation="horizontal" class="w-[90%]" />
<Collapsible.Root class="w-[90%] space-y-2">
<Collapsible.Trigger asChild let:builder>
<Button builders={[builder]} variant="ghost" size="sm" class="w-full p-0 justify-start">
{#if builder["data-state"] == "open"}
<CaretDown />
{:else}
<CaretRight />
{/if}
<h4 class="rounded-md text-sm font-semibold text-left align-middle">
Games
</h4>
</Button>
</Collapsible.Trigger>
<Collapsible.Content class="space-y-2">
{#each games as game}
<div class="pl-4 flex items-center space-x-2">
<Checkbox id="{game.id.toString()}" checked={game.checked} on:click={() => game.checked = !game.checked} />
<Label
for="{game.id.toString()}"
class=""
>
{game.name}
</Label>
</div>
{/each}
</Collapsible.Content>
</Collapsible.Root>
<Separator orientation="horizontal" class="w-[90%]" />
{:else if modeSelect.value == modes[4].value}
<Separator orientation="horizontal" class="w-[90%]" />
<Collapsible.Root class="w-[90%] space-y-2">
<Collapsible.Trigger asChild let:builder>
<Button builders={[builder]} variant="ghost" size="sm" class="w-full p-0 justify-start">
{#if builder["data-state"] == "open"}
<CaretDown />
{:else}
<CaretRight />
{/if}
<h4 class="rounded-md text-sm font-semibold text-left align-middle">
Games
</h4>
</Button>
</Collapsible.Trigger>
<Collapsible.Content class="space-y-2">
{#each games as game}
<div class="pl-4 flex items-center space-x-2">
<Checkbox id="{game.id.toString()}" checked={game.checked} on:click={() => game.checked = !game.checked} />
<Label
for="{game.id.toString()}"
class=""
>
{game.name}
</Label>
</div>
{/each}
</Collapsible.Content>
</Collapsible.Root>
<Separator orientation="horizontal" class="w-[90%]" />
{/if}
</div>
<div class="pt-6 px-3 flex flex-col col-span-2 items-center border-r-2 border-accent text-center text-muted-foreground gap-2 overflow-y-scroll">
{#if modeSelect.value == modes[0].value}
{#each games as game}
<!--<Skeleton class="h-12 w-[90%]" />-->
<div class="px-6 h-12 w-[90%] flex flex-row items-center border-2 rounded-md bg-primary/10">
<p class="text-primary">{game.name}</p>
</div>
{:else}
No Content
{/each}
<Button
class="px-6 h-12 w-[90%] flex flex-row items-center border-2 rounded-md bg-primary/10 text-primary hover:bg-primary/5"
on:click={() => {games.push(newGame())}}
>
Add Game
</Button>
{:else if modeSelect.value == modes[1].value}
{#each filteredGames as game}
{#each game.groups as group}
<Skeleton class="h-12 w-[90%]" />
{/each}
{:else}
No Content
{/each}
<Button>Add Group</Button>
{:else if modeSelect.value == modes[2].value}
{console.log(filteredGroups)}
{#each filteredGroups as group}
{#each group.empires as empire}
<Skeleton class="h-12 w-[90%]" />
{/each}
{:else}
No Content
{/each}
<Button>Add Empire</Button>
{:else if modeSelect.value == modes[3].value}
{#each filteredGames as game}
<Skeleton class="h-12 w-[90%]" />
{:else}
No Content
{/each}
<Button>Add Ethic</Button>
{:else if modeSelect.value == modes[4].value}
{#each filteredGames as game}
<Skeleton class="h-12 w-[90%]" />
{:else}
No Content
{/each}
<Button>Add Portrait</Button>
{/if}
</div>
<div class="pt-6 px-3 flex flex-col col-span-2 text-center text-muted-foreground">
No Content
</div>
</div>

View file

@ -0,0 +1,6 @@
<script lang="ts">
</script>
<div>
PREVIOUS GAMES PAGE
</div>

View file

@ -0,0 +1,8 @@
<script lang="ts">
import type { PageData } from './$types'
export let data: PageData;
</script>
<div>
PREVIOUS GAME {data.gameId} PAGE
</div>

View file

@ -0,0 +1,8 @@
import { error } from '@sveltejs/kit';
/** @type {import('./$types').PageLoad} */
export function load({ params }) {
return {
gameId: params.gameId
}
}

View file

@ -0,0 +1,3 @@
<div>
CURRENT GAME PAGE
</div>

View file

@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
import SubNav from './SubNav.svelte'; import SubNav from './SubNav.svelte';
import '../../app.postcss'; import '../../../app.postcss';
import { goto } from "$app/navigation"; import { goto } from "$app/navigation";
import { page } from "$app/stores"; import { page } from "$app/stores";
import GraphsTabStore from '$lib/stores/GraphsTab'; import GraphsTabStore from '$lib/stores/GraphsTab';

View file

@ -2,9 +2,9 @@ import ChellarisDataStore from '$lib/stores/ChellarisData';
import SelectedGameStore from '$lib/stores/GameFilter'; import SelectedGameStore from '$lib/stores/GameFilter';
import SelectedGameGroupsStore, { type SelectedChellarisGroups } from "$lib/stores/GameGroupFilter"; import SelectedGameGroupsStore, { type SelectedChellarisGroups } from "$lib/stores/GameGroupFilter";
import GraphsTabStore from '$lib/stores/GraphsTab'; import GraphsTabStore from '$lib/stores/GraphsTab';
import type { LayoutLoad } from "./$types"; import type { LayoutLoad } from "../../../../.svelte-kit/types/src/routes";
import type { ChellarisInfo } from '../../lib/types/chellaris'; import type { ChellarisInfo } from '$lib/types/chellaris';
import { apiBaseUrl } from '$lib/components/consts'; import { apiBaseUrl } from '$lib/components_custom/consts';
export const load: LayoutLoad = async ({ fetch }) => { export const load: LayoutLoad = async ({ fetch }) => {
let store: string | null; let store: string | null;

View file

@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
import DropDown from '$lib/components/DropDown.svelte'; import DropDown from '$lib/components_custom/DropDown.svelte';
import DropDownElement from '$lib/components/DropDownElement.svelte'; import DropDownElement from '$lib/components_custom/DropDownElement.svelte';
import SelectedGameGroupsStore from '$lib/stores/GameGroupFilter'; import SelectedGameGroupsStore from '$lib/stores/GameGroupFilter';
import SelectedGameStore from '$lib/stores/GameFilter'; import SelectedGameStore from '$lib/stores/GameFilter';
import ChellarisDataStore from '$lib/stores/ChellarisData'; import ChellarisDataStore from '$lib/stores/ChellarisData';

View file

@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
import DropDown from '$lib/components/DropDown.svelte'; import DropDown from '$lib/components_custom/DropDown.svelte';
import DropDownElement from '$lib/components/DropDownElement.svelte'; import DropDownElement from '$lib/components_custom/DropDownElement.svelte';
import SelectedGameStore from '$lib/stores/GameFilter'; import SelectedGameStore from '$lib/stores/GameFilter';
import ChellarisDataStore from '$lib/stores/ChellarisData'; import ChellarisDataStore from '$lib/stores/ChellarisData';

View file

@ -4,7 +4,7 @@
import type { ChellarisGameGroup, ChellarisInfo, ChellarisEmpire, ChellarisGame } from '$lib/types/chellaris'; import type { ChellarisGameGroup, ChellarisInfo, ChellarisEmpire, ChellarisGame } from '$lib/types/chellaris';
import ChellarisDataStore from '$lib/stores/ChellarisData'; import ChellarisDataStore from '$lib/stores/ChellarisData';
import GraphsTabStore from '$lib/stores/GraphsTab'; import GraphsTabStore from '$lib/stores/GraphsTab';
import LoadingSpinner from '$lib/components/LoadingSpinner.svelte'; import LoadingSpinner from '$lib/components_custom/LoadingSpinner.svelte';
$: selectedGame = $ChellarisDataStore.games[$SelectedGameStore]; $: selectedGame = $ChellarisDataStore.games[$SelectedGameStore];
$: selectedGameGroupData = $SelectedGameGroupsStore[$SelectedGameStore]; $: selectedGameGroupData = $SelectedGameGroupsStore[$SelectedGameStore];

View file

@ -3,7 +3,7 @@
import EthicsBar from './EthicsBar.svelte'; import EthicsBar from './EthicsBar.svelte';
import PopsPie from './PopsPie.svelte'; import PopsPie from './PopsPie.svelte';
import EmpireStats from './EmpireStats.svelte'; import EmpireStats from './EmpireStats.svelte';
import LoadingSpinner from '$lib/components/LoadingSpinner.svelte'; import LoadingSpinner from '$lib/components_custom/LoadingSpinner.svelte';
import { navigating } from '$app/stores'; import { navigating } from '$app/stores';
import { fade } from 'svelte/transition'; import { fade } from 'svelte/transition';

View file

@ -1,5 +1,5 @@
import { apiBaseUrl } from "$lib/components/consts"; import { apiBaseUrl } from "$lib/components_custom/consts";
import type { PageLoad } from "../$types"; import type { PageLoad } from "../../../../.svelte-kit/types/src/routes";
export const load: PageLoad = async ({ fetch }) => { export const load: PageLoad = async ({ fetch }) => {
const popsRet: { speciesArray: Array<number> } = await (await fetch(apiBaseUrl + '/v1/species')).json(); const popsRet: { speciesArray: Array<number> } = await (await fetch(apiBaseUrl + '/v1/species')).json();

View file

@ -0,0 +1,10 @@
<svelte:head>
<title>Empire Sign-Up</title>
<meta name="description" content="Empire Sign-Up" />
</svelte:head>
<div class="text-column">
<h1>Empire Sign-Up: Not yet implemented</h1>
<p>For now only Sign-Up statistics and the backend have been migrated.</p>
</div>

View file

Some files were not shown because too many files have changed in this diff Show more