app: refactor account menu to use Dropdown component

This commit is contained in:
Kmc
2024-06-16 00:20:13 -04:00
parent ef2ed0b69f
commit 4282ee2a7f
4 changed files with 184 additions and 105 deletions

View File

@@ -1,5 +1,5 @@
<script lang="ts">
import { onDestroy, onMount } from 'svelte';
import { onMount } from 'svelte';
import { boltSub } from '@/main';
import { accountList, config, credentials, isConfigDirty, selectedPlay } from '$lib/Util/store';
import type { Credentials } from '$lib/Util/interfaces';
@@ -11,29 +11,14 @@
} from '$lib/Util/functions';
import { logger } from '$lib/Util/Logger';
// props
export let showAccountDropdown: boolean;
export let hoverAccountButton: boolean;
// values gather from s()
const sOrigin = atob(boltSub.origin);
const clientId = atob(boltSub.clientid);
const exchangeUrl = sOrigin.concat('/oauth2/token');
const revokeUrl = sOrigin.concat('/oauth2/revoke');
let mousedOver: boolean = false;
let accountSelect: HTMLSelectElement;
// callback function for 'mousedown' events
// closes the dropdown, unless they press the button that triggers it
function checkClickOutside(evt: MouseEvent): void {
if (hoverAccountButton) return;
if (evt.button === 0 && showAccountDropdown && !mousedOver) {
showAccountDropdown = false;
}
}
// upate selected_play and account_list when logout is clicked
function logoutClicked(): void {
if (accountSelect.options.length == 0) {
@@ -109,9 +94,6 @@
}
}
// check if user clicks outside of the dropdown,
addEventListener('mousedown', checkClickOutside);
// checks the config and updates the selected_play and select
onMount(() => {
let index: number = 0;
@@ -124,53 +106,36 @@
$selectedPlay.credentials = $credentials.get(<string>$selectedPlay.account?.userId);
});
// When the component is removed, delete the event listener also
onDestroy(() => {
removeEventListener('mousedown', checkClickOutside);
});
</script>
<div
class="z-10 w-48 rounded-lg border-2 border-slate-300 bg-slate-100 p-3 shadow dark:border-slate-800 dark:bg-slate-900"
role="none"
on:mouseenter={() => {
mousedOver = true;
}}
on:mouseleave={() => {
mousedOver = false;
<select
name="account_select"
id="account_select"
class="w-full cursor-pointer rounded-lg border-2 border-inherit bg-inherit p-2 text-center"
bind:this={accountSelect}
on:change={() => {
accountChanged();
}}
>
<select
name="account_select"
id="account_select"
class="w-full cursor-pointer rounded-lg border-2 border-inherit bg-inherit p-2 text-center"
bind:this={accountSelect}
on:change={() => {
accountChanged();
{#each $accountList as account}
<option data-id={account[1].userId} class="dark:bg-slate-900">{account[1].displayName}</option>
{/each}
</select>
<div class="mt-5 flex">
<button
class="mx-auto mr-2 rounded-lg bg-blue-500 p-2 font-bold text-black duration-200 hover:opacity-75"
on:click={() => {
loginClicked();
}}
>
{#each $accountList as account}
<option data-id={account[1].userId} class="dark:bg-slate-900">{account[1].displayName}</option
>
{/each}
</select>
<div class="mt-5 flex">
<button
class="mx-auto mr-2 rounded-lg bg-blue-500 p-2 font-bold text-black duration-200 hover:opacity-75"
on:click={() => {
loginClicked();
}}
>
Log In
</button>
<button
class="mx-auto rounded-lg border-2 border-blue-500 p-2 font-bold duration-200 hover:opacity-75"
on:click={() => {
logoutClicked();
}}
>
Log Out
</button>
</div>
Log In
</button>
<button
class="mx-auto rounded-lg border-2 border-blue-500 p-2 font-bold duration-200 hover:opacity-75"
on:click={() => {
logoutClicked();
}}
>
Log Out
</button>
</div>

View File

@@ -0,0 +1,94 @@
<script lang="ts">
import clickOutside from '$lib/Util/ClickOutside';
let className = '';
export { className as class };
export let position: 'top' | 'right' | 'bottom' | 'left' = 'bottom';
export let align: 'start' | 'center' | 'end' = 'start';
let isOpen = false;
let openButton: HTMLButtonElement;
export function open() {
isOpen = true;
}
export function close() {
isOpen = false;
}
function toggle() {
isOpen ? close() : open();
}
</script>
<div class="relative h-fit w-fit {className}">
<button bind:this={openButton} on:click={toggle}><slot /></button>
{#if isOpen}
<div
class="dropdown-color absolute z-20 rounded-lg border-2 {position} {align}"
use:clickOutside={{ callback: close, ignore: [openButton] }}
>
<slot name="content" />
</div>
{/if}
</div>
<style lang="postcss">
.dropdown-color {
@apply border-slate-300 bg-slate-100 p-3 shadow dark:border-slate-800 dark:bg-slate-900;
}
.top {
bottom: 100%;
@apply mb-1;
}
.bottom {
top: 100%;
@apply mt-1;
}
.top.start,
.bottom.start {
left: 0;
}
.top.center,
.bottom.center {
left: 50%;
transform: translateX(-50%);
}
.top.end,
.bottom.end {
right: 0;
}
.left {
right: 100%;
@apply mr-1;
}
.right {
left: 100%;
@apply ml-1;
}
.left.start,
.right.start {
top: 0;
}
.left.center,
.right.center {
top: 50%;
transform: translateY(-50%);
}
.left.end,
.right.end {
bottom: 0;
}
</style>

View File

@@ -1,17 +1,15 @@
<script lang="ts">
import { onMount } from 'svelte';
import { loginClicked } from '$lib/Util/functions';
import { Game } from '$lib/Util/interfaces';
import { config, isConfigDirty, selectedPlay } from '$lib/Util/store';
import Account from '$lib/Components/Account.svelte';
import SettingsModal from '$lib/Components/SettingsModal.svelte';
import { loginClicked } from '$lib/Util/functions';
import Dropdown from '$lib/Components/CommonUI/Dropdown.svelte';
import Account from '$lib/Components/Account.svelte';
let settingsModal: SettingsModal;
let showAccountDropdown: boolean = false;
let hoverAccountButton: boolean = false;
let rs3Button: HTMLButtonElement;
let osrsButton: HTMLButtonElement;
let accountButton: HTMLButtonElement;
// tailwind can easily change theme by adding or removing 'dark' to the root 'html' element
function change_theme(): void {
@@ -43,16 +41,6 @@
}
}
// if no account is signed in, open auth window
// else, toggle the account dropdown
function toggle_account(): void {
if (accountButton.innerHTML == 'Log In') {
loginClicked();
return;
}
showAccountDropdown = !showAccountDropdown;
}
onMount(() => {
toggle_game(<Game>$config.selected_game_index);
});
@@ -83,41 +71,38 @@
OSRS
</button>
</div>
<div class="ml-auto flex">
<div class="m-2 ml-auto flex gap-2">
<button
class="my-3 h-10 w-10 rounded-full bg-blue-500 p-2 duration-200 hover:rotate-45 hover:opacity-75"
class="h-10 w-10 rounded-full bg-blue-500 text-center duration-200 hover:rotate-45 hover:opacity-75"
on:click={() => change_theme()}
>
<img src="svgs/lightbulb-solid.svg" class="h-6 w-6" alt="Change Theme" />
<img src="svgs/lightbulb-solid.svg" class="m-auto h-6 w-6" alt="Change Theme" />
</button>
<button
class="m-3 h-10 w-10 rounded-full bg-blue-500 p-2 duration-200 hover:rotate-45 hover:opacity-75"
class="h-10 w-10 rounded-full bg-blue-500 text-center duration-200 hover:rotate-45 hover:opacity-75"
on:click={() => settingsModal.open()}
>
<img src="svgs/gear-solid.svg" class="h-6 w-6" alt="Settings" />
<img src="svgs/gear-solid.svg" class="m-auto h-6 w-6" alt="Settings" />
</button>
<button
class="m-2 w-48 rounded-lg border-2 border-slate-300 bg-inherit p-2 text-center font-bold text-black duration-200 hover:opacity-75 dark:border-slate-800 dark:text-slate-50"
bind:this={accountButton}
on:mouseenter={() => {
hoverAccountButton = true;
}}
on:mouseleave={() => {
hoverAccountButton = false;
}}
on:click={() => toggle_account()}
>
{#if $selectedPlay.account}
{$selectedPlay.account?.displayName}
{:else}
Log In
{/if}
</button>
</div>
{#if $selectedPlay.account}
<Dropdown align="center">
<button
class="h-11 w-48 rounded-lg border-2 border-slate-300 bg-inherit text-center font-bold text-black duration-200 hover:opacity-75 dark:border-slate-800 dark:text-slate-50"
>
{$selectedPlay.account?.displayName}
</button>
{#if showAccountDropdown}
<div class="absolute right-2 top-[72px]">
<Account bind:showAccountDropdown {hoverAccountButton}></Account>
</div>
{/if}
<div slot="content" class="w-40">
<Account />
</div>
</Dropdown>
{:else}
<button
class="h-11 w-48 rounded-lg border-2 border-slate-300 bg-inherit p-2 text-center font-bold text-black duration-200 hover:opacity-75 dark:border-slate-800 dark:text-slate-50"
on:click={loginClicked}
>
Login
</button>
{/if}
</div>
</div>

View File

@@ -0,0 +1,35 @@
interface IOptions {
callback: () => void;
ignore?: HTMLElement[];
}
// https://github.com/Th1nkK1D/svelte-use-click-outside
function clickOutside(
node: HTMLElement,
{ callback, ignore = [] }: IOptions
): { destroy: () => void } {
const onClick = (event: MouseEvent) => {
const targetIsIgnored = ignore.some((el) => el.contains(event.target as HTMLElement));
if (
node &&
!node.contains(event.target as HTMLElement) &&
!event.defaultPrevented &&
!targetIsIgnored
) {
callback();
}
};
document.addEventListener('click', onClick, true);
document.addEventListener('contextmenu', onClick, true);
return {
destroy() {
document.removeEventListener('click', onClick, true);
document.removeEventListener('contextmenu', onClick, true);
}
};
}
export default clickOutside;