mirror of
https://github.com/Adamcake/Bolt.git
synced 2026-04-22 18:06:52 -04:00
app: refactor account menu to use Dropdown component
This commit is contained in:
@@ -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>
|
||||
|
||||
94
app/src/lib/Components/CommonUI/Dropdown.svelte
Normal file
94
app/src/lib/Components/CommonUI/Dropdown.svelte
Normal 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>
|
||||
@@ -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>
|
||||
|
||||
35
app/src/lib/Util/ClickOutside.ts
Normal file
35
app/src/lib/Util/ClickOutside.ts
Normal 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;
|
||||
Reference in New Issue
Block a user