🔀 Merged ClassQuizController

This commit is contained in:
Mawoka
2023-06-30 01:36:48 +02:00
183 changed files with 10232 additions and 3260 deletions
@@ -18,7 +18,11 @@
<a
{href}
{target}
class="text-black hover:bg-opacity-80 w-full px-4 py-2 leading-5 text-black transition-all duration-200 transform bg-[#B07156] rounded text-center focus:outline-none disabled:cursor-not-allowed disabled:opacity-50 outline-none"
{disabled}
class:opacity-50={disabled}
class:cursor-not-allowed={disabled}
class:pointer-events-none={disabled}
class="text-black hover:bg-opacity-80 w-full px-4 py-2 leading-5 transition-all duration-200 transform bg-[#B07156] rounded text-center outline-none"
on:click
class:flex
class:justify-center={flex}
@@ -29,7 +33,7 @@
<button
{disabled}
{type}
class="text-black hover:opacity-80 w-full px-4 py-2 leading-5 text-black transition-all duration-200 transform bg-[#B07156] rounded text-center focus:outline-none disabled:cursor-not-allowed disabled:opacity-50 outline-none"
class="text-black hover:opacity-80 w-full px-4 py-2 leading-5 transition-all duration-200 transform bg-[#B07156] rounded text-center focus:outline-none disabled:cursor-not-allowed disabled:opacity-50 outline-none"
on:click
class:flex
class:justify-center={flex}
@@ -0,0 +1,279 @@
<!--
- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at https://mozilla.org/MPL/2.0/.
-->
<script lang="ts">
import { onMount } from 'svelte';
import { tinykeys } from '$lib/tinykeys';
import { fade } from 'svelte/transition';
import MiniSearch from 'minisearch';
let open = false;
let input = '';
let bg_text = '';
let title_ms: MiniSearch;
let command_ms: MiniSearch;
let selected: null | number = null;
// eslint-disable-next-line no-unused-vars
type ActionFunction = (args: string[]) => void;
const actions: {
id: number;
title: string;
description?: string;
command?: string;
args?: string[];
action: ActionFunction;
}[] = [
{
id: 0,
title: 'Close CommandPalette',
description: 'Closes CommandPalette',
command: 'close',
action: () => close_cp(undefined)
},
{
id: 1,
title: 'Create Quiz',
description: 'Opens editor to create a new quiz',
command: 'newquiz',
args: ['title'],
action: (args) => window.location.assign(`/create?title=${args.join(' ')}`)
},
{
id: 2,
title: 'Import a Quiz',
description: 'Opens the import page',
command: 'import',
args: ['url'],
action: (args) => window.location.assign(`/import?url=${args?.[0] ?? ''}`)
},
{
id: 3,
title: 'Create Quiztivity',
description: 'Opens the editor for quiztivities',
command: 'newquiztivity',
args: ['title'],
action: (args) => window.location.assign(`/quiztivity/create?title=${args.join(' ')}`)
},
{
id: 4,
title: 'View Results',
description: 'Opens the Results viewer',
command: 'results',
action: () => window.location.assign('/results')
},
{
id: 5,
title: 'Explore Quizzes',
description: 'Opens the Explore-page',
command: 'explore',
action: () => window.location.assign('/explore')
},
{
id: 6,
title: 'Dashboard',
description: 'Go to Dashboard',
command: 'dash',
action: () => window.location.assign('/dashboard')
},
{
id: 7,
title: 'Docs',
description: 'Go to documentation',
command: 'docs',
action: () => window.location.assign('/docs')
},
{
id: 8,
title: 'Settings',
description: 'Opens the Settings page',
command: 'settings',
action: () => window.location.assign('/account/settings')
}
];
let visible_items = actions;
const toggle_open = (e: KeyboardEvent | undefined) => {
e.preventDefault();
open = !open;
console.log('TOGGLE!');
};
const close_cp = (e: KeyboardEvent | undefined) => {
if (e) {
e.preventDefault();
}
open = false;
};
const close_on_outside = (e: Event) => {
if (e.target == e.currentTarget) {
open = false;
}
};
const execute_action = () => {
let args = [];
const entry = visible_items[selected];
if (input.startsWith('/')) {
const tokens = input.split(' ');
args = tokens.slice(1);
}
console.log(args);
entry.action(args);
};
const search = (term: string) => {
if (!command_ms || !title_ms) {
return;
}
if (term === '' || term === '/') {
selected = 0;
visible_items = actions;
bg_text = '';
return;
}
let suggestions;
let res;
if (term.startsWith('/')) {
term = term.substring(1);
suggestions = command_ms.autoSuggest(term, { boost: { command: 2 }, prefix: true });
res = command_ms.search(term, { boost: { command: 2 }, prefix: true });
bg_text = suggestions[0]?.suggestion;
bg_text ??= '';
bg_text = `/${bg_text}`;
} else {
suggestions = title_ms.autoSuggest(term, { boost: { command: 2 }, prefix: true });
res = title_ms.search(term, { boost: { command: 2 }, prefix: true });
bg_text = suggestions[0]?.suggestion;
bg_text ??= '';
}
visible_items = [];
console.log(res);
for (const quiz_data of res) {
visible_items.push(actions[quiz_data.id]);
}
visible_items = visible_items;
if (visible_items.length === 1) {
selected = 0;
}
if (visible_items.length === 0) {
selected = null;
}
};
const autocomplete_on_tab = (e: KeyboardEvent) => {
e.preventDefault();
input = bg_text;
};
const on_arrow_down = (e: KeyboardEvent) => {
e.preventDefault();
if (visible_items.length < 1) {
return;
}
if (selected + 1 === visible_items.length) {
return;
}
selected += 1;
};
const on_arrow_up = (e: KeyboardEvent) => {
e.preventDefault();
if (visible_items.length < 1) {
return;
}
if (selected === 0) {
return;
}
selected -= 1;
};
const on_enter = (e: KeyboardEvent) => {
e.preventDefault();
if (selected === null) {
return;
}
execute_action();
input = '';
};
$: search(input);
// $: input = lower_input(input)
$: input = input.toLowerCase();
onMount(async () => {
tinykeys(window, {
'$mod+k': toggle_open,
Escape: close_cp,
Tab: autocomplete_on_tab,
ArrowDown: on_arrow_down,
ArrowUp: on_arrow_up,
Enter: on_enter
});
title_ms = new MiniSearch<any>({
fields: ['title'],
storeFields: ['id']
});
title_ms.addAll(actions);
command_ms = new MiniSearch<any>({
fields: ['command'],
storeFields: ['id']
});
command_ms.addAll(actions);
});
</script>
{#if open}
<div
class="fixed top-0 left-0 w-screen h-screen flex bg-black bg-opacity-50 z-50"
on:click={close_on_outside}
transition:fade={{ duration: 60 }}
>
<div class="m-auto w-1/3 h-2/3 rounded bg-black flex flex-col">
<div class="grid grid-cols-1 grid-rows-1 border-b border-b-white">
<p
class="col-start-1 row-start-1 w-full p-4 outline-none bg-gray-700 rounded-t text-gray-400"
>
{bg_text}
</p>
<input
type="text"
autofocus
class="col-start-1 row-start-1 bg-transparent w-full p-4 outline-none bg-gray-700 rounded"
bind:value={input}
/>
</div>
<div class="flex flex-col p-2 gap-2 overflow-scroll">
{#each visible_items as vi, i}
<div
transition:fade|local={{ duration: 60 }}
class="p-2 transition rounded"
class:bg-[#B07156]={selected === i}
class:bg-gray-700={selected !== i}
on:mouseenter={() => (selected = i)}
on:mousedown={execute_action}
>
<div class="flex">
<h3 class="text-lg my-auto">{vi.title}</h3>
<p
class="font-mono my-auto ml-auto h-fit bg-black bg-opacity-50 rounded p-0.5"
>
/{vi.command}
{#if vi.args}
{#each vi.args as arg}
&lbrace;<span class="text-indigo-400">{arg}</span
>&rbrace;{/each}
{/if}
</p>
</div>
<p class="text-sm">{vi.description}</p>
</div>
{/each}
</div>
</div>
</div>
{/if}
@@ -0,0 +1,61 @@
<!--
- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at https://mozilla.org/MPL/2.0/.
-->
<script lang="ts">
import { fly } from 'svelte/transition';
import { getLocalization } from '$lib/i18n';
import { PopoverTypes } from './smalltop';
const { t } = getLocalization();
export let open = false;
export let type: PopoverTypes;
export let data: undefined | { game_pin: number | string; game_id: string } = undefined;
</script>
{#if open}
<div class="fixed w-screen top-10 z-[60] flex justify-center" transition:fly={{ y: -100 }}>
<div
class="flex items-center p-4 w-full max-w-xs text-gray-500 bg-white rounded-lg shadow dark:text-gray-400 dark:bg-gray-800"
role="alert"
>
<div class="ml-3 text-sm font-normal">
{#if type === PopoverTypes.Copy}
{$t('components.popover.copied_to_clipboard')}
{:else if type === PopoverTypes.GameInLobby}A game is currently in the lobby. Click <a
class="underline"
href="/remote?game_pin={data.game_pin}&game_id={data.game_id}">here</a
> to join as a remote.
{:else}
<p>Error!!!</p>
{/if}
</div>
<button
type="button"
class="ml-auto -mx-1.5 -my-1.5 bg-white text-gray-400 hover:text-gray-900 rounded-lg focus:ring-2 focus:ring-gray-300 p-1.5 hover:bg-gray-100 inline-flex h-8 w-8 dark:text-gray-500 dark:hover:text-white dark:bg-gray-800 dark:hover:bg-gray-700"
data-dismiss-target="#toast-default"
aria-label="Close"
on:click={() => {
open = false;
}}
>
<span class="sr-only">{$t('words.close')}</span>
<svg
aria-hidden="true"
class="w-5 h-5"
fill="currentColor"
viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg"
>
<path
fill-rule="evenodd"
d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z"
clip-rule="evenodd"
/>
</svg>
</button>
</div>
</div>
{/if}
@@ -0,0 +1,11 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
/* eslint-disable no-unused-vars */
export enum PopoverTypes {
Copy,
GameInLobby
}