CSS Grid + Admin Page Content
This commit is contained in:
parent
6135d18366
commit
3992d964da
19 changed files with 430 additions and 306 deletions
1
src/lib/components/consts.ts
Normal file
1
src/lib/components/consts.ts
Normal file
|
@ -0,0 +1 @@
|
|||
export const apiBaseUrl = 'https://wip.chellaris.net/api';
|
5
src/lib/stores/AuthTokenStore.ts
Normal file
5
src/lib/stores/AuthTokenStore.ts
Normal file
|
@ -0,0 +1,5 @@
|
|||
import { writable, type Writable } from "svelte/store";
|
||||
|
||||
const AuthTokenStore: Writable<string> = writable("");
|
||||
|
||||
export default AuthTokenStore;
|
5
src/lib/stores/admin-page/EmpireStore.ts
Normal file
5
src/lib/stores/admin-page/EmpireStore.ts
Normal file
|
@ -0,0 +1,5 @@
|
|||
import { writable, type Writable } from "svelte/store";
|
||||
|
||||
const AdminSelectedEmpireStore: Writable<{ [key: number]: number }> = writable({});
|
||||
|
||||
export default AdminSelectedEmpireStore;
|
7
src/lib/stores/admin-page/GameDataStore.ts
Normal file
7
src/lib/stores/admin-page/GameDataStore.ts
Normal file
|
@ -0,0 +1,7 @@
|
|||
import { writable, type Writable } from "svelte/store";
|
||||
import type { ChellarisInfo } from '../../types/chellaris';
|
||||
import { createChellarisInfo } from '../../types/chellaris';
|
||||
|
||||
const AdminGameDataStore: Writable<ChellarisInfo> = writable(createChellarisInfo());
|
||||
|
||||
export default AdminGameDataStore;
|
5
src/lib/stores/admin-page/GameStore.ts
Normal file
5
src/lib/stores/admin-page/GameStore.ts
Normal file
|
@ -0,0 +1,5 @@
|
|||
import { writable, type Writable } from "svelte/store";
|
||||
|
||||
const AdminSelectedGameStore: Writable<number> = writable();
|
||||
|
||||
export default AdminSelectedGameStore;
|
|
@ -29,6 +29,11 @@ export type ChellarisEmpire = {
|
|||
ethics: { [key: number]: Ethic },
|
||||
}
|
||||
|
||||
export type ChellarisGameInfo = {
|
||||
id: number,
|
||||
name: string
|
||||
}
|
||||
|
||||
export const createChellarisInfo = (): ChellarisInfo => {
|
||||
const newChellarisInfo = {
|
||||
games: [],
|
||||
|
|
|
@ -11,19 +11,12 @@
|
|||
|
||||
<style>
|
||||
.app {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
display: grid;
|
||||
grid-template-areas:
|
||||
'header'
|
||||
'app';
|
||||
grid-template-rows: 3rem 1fr;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
main {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 0rem;
|
||||
width: 100%;
|
||||
max-height: 95vh;
|
||||
margin: 0 auto;
|
||||
box-sizing: border-box;
|
||||
max-height: 100vh;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -53,6 +53,7 @@
|
|||
|
||||
<style>
|
||||
header {
|
||||
grid-area: header;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<script lang="ts">
|
||||
import { browser } from "$app/environment";
|
||||
import Modal from "$lib/components/Modal.svelte";
|
||||
import AuthTokenStore from "$lib/stores/AuthTokenStore";
|
||||
import { fade } from "svelte/transition";
|
||||
|
||||
export let showSettings: boolean;
|
||||
|
@ -13,13 +14,14 @@
|
|||
showAuthSaved = false;
|
||||
}, 2000);
|
||||
document.cookie = "authToken=" + authToken;
|
||||
$AuthTokenStore = authToken;
|
||||
}
|
||||
|
||||
let authToken: string;
|
||||
$: console.log(authToken);
|
||||
$: {
|
||||
if (browser) {
|
||||
authToken = document.cookie.split("=")[document.cookie.split("=").length - 1];
|
||||
$AuthTokenStore = authToken;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,69 +0,0 @@
|
|||
import { fail } from '@sveltejs/kit';
|
||||
import { Game } from './game';
|
||||
import type { PageServerLoad, Actions } from './$types';
|
||||
|
||||
export const load = (({ cookies }) => {
|
||||
const game = new Game(cookies.get('sverdle'));
|
||||
|
||||
return {
|
||||
/**
|
||||
* The player's guessed words so far
|
||||
*/
|
||||
guesses: game.guesses,
|
||||
|
||||
/**
|
||||
* An array of strings like '__x_c' corresponding to the guesses, where 'x' means
|
||||
* an exact match, and 'c' means a close match (right letter, wrong place)
|
||||
*/
|
||||
answers: game.answers,
|
||||
|
||||
/**
|
||||
* The correct answer, revealed if the game is over
|
||||
*/
|
||||
answer: game.answers.length >= 6 ? game.answer : null
|
||||
};
|
||||
}) satisfies PageServerLoad;
|
||||
|
||||
export const actions = {
|
||||
/**
|
||||
* Modify game state in reaction to a keypress. If client-side JavaScript
|
||||
* is available, this will happen in the browser instead of here
|
||||
*/
|
||||
update: async ({ request, cookies }) => {
|
||||
const game = new Game(cookies.get('sverdle'));
|
||||
|
||||
const data = await request.formData();
|
||||
const key = data.get('key');
|
||||
|
||||
const i = game.answers.length;
|
||||
|
||||
if (key === 'backspace') {
|
||||
game.guesses[i] = game.guesses[i].slice(0, -1);
|
||||
} else {
|
||||
game.guesses[i] += key;
|
||||
}
|
||||
|
||||
cookies.set('sverdle', game.toString());
|
||||
},
|
||||
|
||||
/**
|
||||
* Modify game state in reaction to a guessed word. This logic always runs on
|
||||
* the server, so that people can't cheat by peeking at the JavaScript
|
||||
*/
|
||||
enter: async ({ request, cookies }) => {
|
||||
const game = new Game(cookies.get('sverdle'));
|
||||
|
||||
const data = await request.formData();
|
||||
const guess = data.getAll('guess') as string[];
|
||||
|
||||
if (!game.enter(guess)) {
|
||||
return fail(400, { badGuess: true });
|
||||
}
|
||||
|
||||
cookies.set('sverdle', game.toString());
|
||||
},
|
||||
|
||||
restart: async ({ cookies }) => {
|
||||
cookies.delete('sverdle');
|
||||
}
|
||||
} satisfies Actions;
|
|
@ -1,121 +1,298 @@
|
|||
<script lang="ts">
|
||||
import { confetti } from '@neoconfetti/svelte';
|
||||
import { enhance } from '$app/forms';
|
||||
import type { PageData, ActionData } from './$types';
|
||||
import { reduced_motion } from './reduced-motion';
|
||||
import { browser } from '$app/environment';
|
||||
import { apiBaseUrl } from '$lib/components/consts';
|
||||
import AuthTokenStore from '$lib/stores/AuthTokenStore';
|
||||
import AdminSelectedEmpireStore from '$lib/stores/admin-page/EmpireStore';
|
||||
import AdminSelectedGameStore from '$lib/stores/admin-page/GameStore';
|
||||
import List from './List.svelte';
|
||||
|
||||
export let data: PageData;
|
||||
export let data;
|
||||
|
||||
export let form: ActionData;
|
||||
|
||||
/** Whether or not the user has won */
|
||||
$: won = data.answers.at(-1) === 'xxxxx';
|
||||
|
||||
/** The index of the current guess */
|
||||
$: i = won ? -1 : data.answers.length;
|
||||
|
||||
/** Whether the current guess can be submitted */
|
||||
$: submittable = data.guesses[i]?.length === 5;
|
||||
|
||||
/**
|
||||
* A map of classnames for all letters that have been guessed,
|
||||
* used for styling the keyboard
|
||||
*/
|
||||
let classnames: Record<string, 'exact' | 'close' | 'missing'>;
|
||||
|
||||
/**
|
||||
* A map of descriptions for all letters that have been guessed,
|
||||
* used for adding text for assistive technology (e.g. screen readers)
|
||||
*/
|
||||
let description: Record<string, string>;
|
||||
let newGameForm = false;
|
||||
let addingNewGame = false;
|
||||
let newGameName = '';
|
||||
$: gameList = data.games;
|
||||
|
||||
$: {
|
||||
classnames = {};
|
||||
description = {};
|
||||
|
||||
data.answers.forEach((answer, i) => {
|
||||
const guess = data.guesses[i];
|
||||
|
||||
for (let i = 0; i < 5; i += 1) {
|
||||
const letter = guess[i];
|
||||
|
||||
if (answer[i] === 'x') {
|
||||
classnames[letter] = 'exact';
|
||||
description[letter] = 'correct';
|
||||
} else if (!classnames[letter]) {
|
||||
classnames[letter] = answer[i] === 'c' ? 'close' : 'missing';
|
||||
description[letter] = answer[i] === 'c' ? 'present' : 'absent';
|
||||
if (typeof localStorage !== 'undefined') {
|
||||
localStorage.setItem('adminGameSelection', JSON.stringify($AdminSelectedGameStore));
|
||||
}
|
||||
};
|
||||
|
||||
$: {
|
||||
if (typeof localStorage !== 'undefined') {
|
||||
localStorage.setItem('adminEmpireSelection', JSON.stringify($AdminSelectedEmpireStore));
|
||||
}
|
||||
};
|
||||
|
||||
const getGameList = () => {
|
||||
fetch(apiBaseUrl + '/v3/list_games').then((response) => response.json().then((result) => (gameList = result)));
|
||||
};
|
||||
|
||||
const setActiveGame = (game: number) => {
|
||||
$AdminSelectedGameStore = game;
|
||||
};
|
||||
|
||||
const addGame = () => {
|
||||
let newGame = {
|
||||
auth: { token: $AuthTokenStore },
|
||||
game_name: newGameName
|
||||
};
|
||||
|
||||
fetch(apiBaseUrl + '/v3/game', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(newGame)
|
||||
}).then((response) => {
|
||||
getGameList();
|
||||
addingNewGame = false;
|
||||
response.json().then((result) => $AdminSelectedGameStore = result.id);
|
||||
});
|
||||
newGameName = '';
|
||||
newGameForm = false;
|
||||
addingNewGame = true;
|
||||
};
|
||||
|
||||
const deleteGame = (game_id: number) => {
|
||||
fetch(apiBaseUrl + "/v3/game?game_id=" + game_id, {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({token: $AuthTokenStore}),
|
||||
}).then(() => {
|
||||
getGameList();
|
||||
if ($AdminSelectedGameStore == game_id) {
|
||||
$AdminSelectedGameStore = Object.values(gameList)[0].id;
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Modify the game state without making a trip to the server,
|
||||
* if client-side JavaScript is enabled
|
||||
*/
|
||||
function update(event: MouseEvent) {
|
||||
const guess = data.guesses[i];
|
||||
const key = (event.target as HTMLButtonElement).getAttribute(
|
||||
'data-key'
|
||||
);
|
||||
const startNewGameForm = () => {
|
||||
newGameForm = true;
|
||||
setActiveGame(Object.values(gameList).length);
|
||||
};
|
||||
|
||||
if (key === 'backspace') {
|
||||
data.guesses[i] = guess.slice(0, -1);
|
||||
if (form?.badGuess) form.badGuess = false;
|
||||
} else if (guess.length < 5) {
|
||||
data.guesses[i] += key;
|
||||
}
|
||||
}
|
||||
const setActiveEmpire = (empireId: number) => {
|
||||
$AdminSelectedEmpireStore[$AdminSelectedGameStore] = empireId;
|
||||
};
|
||||
|
||||
/**
|
||||
* Trigger form logic in response to a keydown event, so that
|
||||
* desktop users can use the keyboard to play the game
|
||||
*/
|
||||
function keydown(event: KeyboardEvent) {
|
||||
if (event.metaKey) return;
|
||||
const addEmpire = () => {
|
||||
$AdminSelectedEmpireStore[$AdminSelectedGameStore] = list2.length;
|
||||
list2.push(list2.length);
|
||||
};
|
||||
|
||||
document
|
||||
.querySelector(`[data-key="${event.key}" i]`)
|
||||
?.dispatchEvent(new MouseEvent('click', { cancelable: true }));
|
||||
let list2 = new Array();
|
||||
for (let i = 0; i < 100; i++) {
|
||||
list2.push(i);
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:window on:keydown={keydown} />
|
||||
|
||||
<svelte:head>
|
||||
<title>Admin Menu</title>
|
||||
<meta name="description" content="Admin Menu for managing Chellaris Sign-Ups" />
|
||||
</svelte:head>
|
||||
|
||||
<div class="text-column">
|
||||
<h1>Admin Menu: Not yet implemented</h1>
|
||||
|
||||
<p>Next on the list after proper graphs</p>
|
||||
<div class="frame">
|
||||
<List area="games" listTitle="Games">
|
||||
{#each Object.values(gameList) as game}
|
||||
<button class="list-card" class:active={game.id == $AdminSelectedGameStore ? 'active' : ''} on:click={() => setActiveGame(game.id)}>
|
||||
<div class="card-content">{game.name}</div>
|
||||
<button class="delete-box" on:click={() => deleteGame(game.id)}>
|
||||
<svg class="checkmark" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<path fill="none" d="M0 0 24 24 M24 0 0 24" />
|
||||
</svg>
|
||||
</button>
|
||||
</button>
|
||||
{/each}
|
||||
{#if newGameForm}
|
||||
<div class="list-card active">
|
||||
<form on:submit={addGame}>
|
||||
<input bind:value={newGameName} />
|
||||
<input type="submit" value="Add Game" />
|
||||
</form>
|
||||
</div>
|
||||
{:else if addingNewGame}
|
||||
<div class="list-card active">
|
||||
<div class="card-content">Adding Game...</div>
|
||||
</div>
|
||||
{:else}
|
||||
<button class="list-card" on:click={startNewGameForm}>
|
||||
<div class="card-content button">+</div>
|
||||
</button>
|
||||
{/if}
|
||||
</List>
|
||||
<List area="empires" listTitle="Empires">
|
||||
<div class="empires-table">
|
||||
<div class="table-headers">
|
||||
<div class="table-header">Empire Name</div>
|
||||
<div class="table-header">Discord User</div>
|
||||
</div>
|
||||
<div class="table-content">
|
||||
{#each list2 as elem}
|
||||
<button class="list-card" class:active={elem == $AdminSelectedEmpireStore[$AdminSelectedGameStore] ? 'active' : ''} on:click={() => setActiveEmpire(elem)}>
|
||||
<div class="card-content" class:active={elem == $AdminSelectedEmpireStore[$AdminSelectedGameStore] ? 'active' : ''}>{elem}</div>
|
||||
<div class="card-content" class:active={elem == $AdminSelectedEmpireStore[$AdminSelectedGameStore] ? 'active' : ''}>{elem}</div>
|
||||
</button>
|
||||
{/each}
|
||||
<button class="list-card" on:click={addEmpire}>
|
||||
<div class="card-content button">+</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</List>
|
||||
<List area="details" listTitle="Empire Details">
|
||||
{$AdminSelectedEmpireStore[$AdminSelectedGameStore]}
|
||||
</List>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
@keyframes wiggle {
|
||||
0% {
|
||||
transform: translateX(0);
|
||||
.frame {
|
||||
display: grid;
|
||||
grid-template-areas: 'games empires details';
|
||||
grid-template-columns: 20% 40% 40%;
|
||||
grid-template-rows: 100%;
|
||||
max-height: calc(100%);
|
||||
overflow: hidden;
|
||||
margin: 3rem;
|
||||
box-sizing: border-box;
|
||||
border: 1px solid white;
|
||||
}
|
||||
10% {
|
||||
transform: translateX(-2px);
|
||||
|
||||
/* General Classes */
|
||||
.active {
|
||||
border-color: darkorange !important;
|
||||
}
|
||||
30% {
|
||||
transform: translateX(4px);
|
||||
|
||||
.list-card {
|
||||
display: flex;
|
||||
margin: 0.5rem;
|
||||
border: 1px solid darkcyan;
|
||||
cursor: pointer;
|
||||
background-color: var(--color-bg);
|
||||
color: var(--color-text);
|
||||
width: calc(100% - 1rem);
|
||||
text-align: left;
|
||||
}
|
||||
50% {
|
||||
transform: translateX(-6px);
|
||||
|
||||
.list-card:hover {
|
||||
border: 1px solid darkorange;
|
||||
}
|
||||
70% {
|
||||
transform: translateX(+4px);
|
||||
|
||||
.list-card form {
|
||||
padding: 4px 9px;
|
||||
}
|
||||
90% {
|
||||
transform: translateX(-2px);
|
||||
|
||||
.list-card input {
|
||||
background-color: var(--color-bg);
|
||||
color: var(--color-text);
|
||||
border: 1px solid darkcyan;
|
||||
font-size: 1rem;
|
||||
padding: 3px 6px;
|
||||
}
|
||||
100% {
|
||||
transform: translateX(0);
|
||||
|
||||
.list-card input:focus {
|
||||
border: 1px solid darkorange;
|
||||
outline: none !important;
|
||||
}
|
||||
|
||||
.list-card input[type='submit']:hover {
|
||||
border: 1px solid darkorange;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.card-content {
|
||||
flex-grow: 1;
|
||||
padding: 0.5rem 1rem;
|
||||
box-sizing: border-box;
|
||||
border-left: 1px solid darkcyan;
|
||||
}
|
||||
|
||||
.button {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.delete-box {
|
||||
background-color: var(--color-bg);
|
||||
width: 1.5rem;
|
||||
height: 1.5rem;
|
||||
justify-self: center;
|
||||
align-self: center;
|
||||
margin-right: 0.2rem;
|
||||
border: 1px solid darkcyan;
|
||||
color: darkcyan;
|
||||
cursor: pointer;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.checkmark {
|
||||
display: block;
|
||||
stroke-width: 2;
|
||||
stroke: darkcyan;
|
||||
stroke-miterlimit: 10;
|
||||
}
|
||||
|
||||
.delete-box:hover .checkmark {
|
||||
stroke: darkorange;
|
||||
}
|
||||
|
||||
.delete-box:hover {
|
||||
color: darkorange;
|
||||
border-color: darkorange;
|
||||
}
|
||||
|
||||
/* Empire Classes */
|
||||
.empires-table {
|
||||
margin: 0.5rem;
|
||||
height: calc(100% - 1rem);
|
||||
overflow: hidden;
|
||||
border: 2px solid blueviolet;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.table-headers {
|
||||
grid-area: empires-header;
|
||||
display: flex;
|
||||
height: 2.3rem;
|
||||
width: calc(100%);
|
||||
padding-right: 12px;
|
||||
box-sizing: border-box;
|
||||
border-bottom: 1px solid orange;
|
||||
justify-items: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.table-header {
|
||||
flex-grow: 1;
|
||||
text-align: center;
|
||||
width: 50%;
|
||||
|
||||
background-color: var(--color-bg);
|
||||
padding: 0.5rem 1.5rem;
|
||||
border-left: 1px solid orange;
|
||||
}
|
||||
|
||||
.table-header:first-child {
|
||||
border-left: none;
|
||||
}
|
||||
|
||||
.table-content {
|
||||
grid-area: empires-list;
|
||||
height: calc(100% - 2.3rem);
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
.list-card:hover .card-content {
|
||||
border-left: 1px solid darkorange;
|
||||
}
|
||||
|
||||
.list-card:hover .card-content:first-child {
|
||||
border-left: none;
|
||||
}
|
||||
|
||||
.card-content:first-child {
|
||||
border-left: none;
|
||||
}
|
||||
</style>
|
||||
|
|
30
src/routes/admin/+page.ts
Normal file
30
src/routes/admin/+page.ts
Normal file
|
@ -0,0 +1,30 @@
|
|||
import { apiBaseUrl } from "$lib/components/consts";
|
||||
import AdminSelectedEmpireStore from "$lib/stores/admin-page/EmpireStore";
|
||||
import AdminSelectedGameStore from "$lib/stores/admin-page/GameStore";
|
||||
import type { ChellarisGameInfo } from "$lib/types/chellaris";
|
||||
|
||||
export async function load ({ fetch }) {
|
||||
const gameList: { [key: number]: ChellarisGameInfo } = await (await fetch(apiBaseUrl + "/v3/list_games")).json();
|
||||
|
||||
let store: string | null;
|
||||
|
||||
if (typeof localStorage !== 'undefined') {
|
||||
// Game Selection
|
||||
store = localStorage.getItem('adminGameSelection');
|
||||
if (typeof store === 'string') {
|
||||
AdminSelectedGameStore.set(JSON.parse(store));
|
||||
}
|
||||
|
||||
// Empire Selection
|
||||
store = localStorage.getItem('adminEmpireSelection');
|
||||
|
||||
if (typeof store === 'string' && store != "\"\"") {
|
||||
AdminSelectedEmpireStore.set(JSON.parse(store));
|
||||
}
|
||||
else if (typeof store === 'string') {
|
||||
AdminSelectedEmpireStore.set({});
|
||||
}
|
||||
}
|
||||
|
||||
return { games: gameList };
|
||||
}
|
43
src/routes/admin/List.svelte
Normal file
43
src/routes/admin/List.svelte
Normal file
|
@ -0,0 +1,43 @@
|
|||
<script lang="ts">
|
||||
export let area = '';
|
||||
export let listTitle = 'List';
|
||||
</script>
|
||||
|
||||
<div class="container" style={'--area:' + area}>
|
||||
<div class="header">
|
||||
{listTitle}
|
||||
</div>
|
||||
<div class="list">
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.container {
|
||||
grid-area: var(--area);
|
||||
display: grid;
|
||||
grid-template-areas:
|
||||
'list-header'
|
||||
'list-content';
|
||||
grid-template-rows: 2rem calc(100% - 2rem);
|
||||
grid-template-columns: 100%;
|
||||
min-height: none;
|
||||
max-height: 100%;
|
||||
}
|
||||
|
||||
.header {
|
||||
grid-area: list-header;
|
||||
line-height: 2;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
border: 1px solid var(--color-text);
|
||||
}
|
||||
|
||||
.list {
|
||||
grid-area: list-content;
|
||||
min-height: none;
|
||||
max-height: 100%;
|
||||
overflow: hidden;
|
||||
border: 1px solid var(--color-text);
|
||||
}
|
||||
</style>
|
|
@ -44,19 +44,16 @@
|
|||
|
||||
<style>
|
||||
.app {
|
||||
display: flex;
|
||||
grid-area: app;
|
||||
display: grid;
|
||||
grid-template-areas:
|
||||
'sub-header'
|
||||
'content';
|
||||
grid-template-rows: 3rem 1fr;
|
||||
flex-direction: column;
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
main {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 2rem;
|
||||
width: 100%;
|
||||
max-height: 100%;
|
||||
margin: 0 auto;
|
||||
box-sizing: border-box;
|
||||
grid-area: content;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -23,83 +23,21 @@
|
|||
<LoadingSpinner size="60" />
|
||||
{:else}
|
||||
<div class="fullscreen-margin">
|
||||
<div class="half-vertical">
|
||||
<div class="two-thirds-horizontal">
|
||||
<EthicsWeb {data} />
|
||||
</div>
|
||||
<div class="one-third-horizontal">
|
||||
<EthicsBar {data} />
|
||||
</div>
|
||||
</div>
|
||||
<div class="half-vertical">
|
||||
<div class="half-horizontal">
|
||||
<EmpireStats {data} />
|
||||
</div>
|
||||
<div class="half-horizontal">
|
||||
<PopsPie {data} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
.fullscreen-margin {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
height: 90vh;
|
||||
flex-grow: 1;
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.half-vertical {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
width: 50%;
|
||||
max-width: 50%;
|
||||
|
||||
min-height: 100%;
|
||||
max-height: 100%;
|
||||
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.half-horizontal {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
|
||||
min-height: 50%;
|
||||
max-height: 50%;
|
||||
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.one-third-horizontal {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
|
||||
min-height: 34%;
|
||||
max-height: 34%;
|
||||
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.two-thirds-horizontal {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
|
||||
min-height: 66%;
|
||||
max-height: 66%;
|
||||
|
||||
justify-content: center;
|
||||
display: grid;
|
||||
grid-template-areas:
|
||||
'uple upri'
|
||||
'lole lori';
|
||||
grid-template-columns: 50% 50%;
|
||||
grid-template-rows: 50% 50%;
|
||||
grid-area: app;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -14,16 +14,8 @@
|
|||
|
||||
<style>
|
||||
.chart {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
min-width: 100%;
|
||||
max-width: 100%;
|
||||
|
||||
min-height: 100%;
|
||||
max-height: 100%;
|
||||
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
grid-area: upri;
|
||||
padding: 2rem;
|
||||
place-self: center;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -61,6 +61,8 @@
|
|||
};
|
||||
|
||||
const options = {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
animation: {
|
||||
duration: 0
|
||||
},
|
||||
|
@ -100,16 +102,7 @@
|
|||
|
||||
<style>
|
||||
.chart {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
min-width: 100%;
|
||||
max-width: 100%;
|
||||
|
||||
min-height: 100%;
|
||||
max-height: 100%;
|
||||
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
grid-area: lole;
|
||||
padding: 2rem;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -96,6 +96,8 @@
|
|||
};
|
||||
|
||||
const options = {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
animation: {
|
||||
duration: 0
|
||||
},
|
||||
|
@ -157,29 +159,33 @@
|
|||
}
|
||||
</script>
|
||||
|
||||
<div class="container">
|
||||
<div class="chart">
|
||||
<Radar data={chart_data} {options} />
|
||||
</div>
|
||||
<label>
|
||||
<input type="checkbox" bind:checked={weighted} on:change={updateChartData} />
|
||||
Use weighted LegacyEthics
|
||||
</label>
|
||||
</div>
|
||||
|
||||
|
||||
<style>
|
||||
.chart {
|
||||
.container {
|
||||
grid-area: uple;
|
||||
padding: 2rem;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
box-sizing: border-box;
|
||||
|
||||
padding: 1rem;
|
||||
|
||||
min-width: 100%;
|
||||
max-width: 100%;
|
||||
|
||||
min-height: 100%;
|
||||
max-height: 100%;
|
||||
gap: 1rem;
|
||||
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.chart {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -42,6 +42,8 @@
|
|||
};
|
||||
|
||||
const options = {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
animation: {
|
||||
duration: 0
|
||||
},
|
||||
|
@ -86,16 +88,7 @@
|
|||
|
||||
<style>
|
||||
.chart {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
min-width: 100%;
|
||||
max-width: 100%;
|
||||
|
||||
min-height: 100%;
|
||||
max-height: 100%;
|
||||
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
grid-area: lori;
|
||||
padding: 2rem;
|
||||
}
|
||||
</style>
|
||||
|
|
Reference in a new issue