Compare commits
2 commits
94e94deb91
...
72c9650f71
Author | SHA1 | Date | |
---|---|---|---|
|
72c9650f71 | ||
|
dc8414c050 |
11 changed files with 264 additions and 299 deletions
|
@ -1,8 +1,22 @@
|
||||||
import { fade, fly, scale } from "svelte/transition";
|
import { fade, fly, scale } from "svelte/transition";
|
||||||
import { cubicInOut } from "svelte/easing";
|
import { cubicInOut, linear } from "svelte/easing";
|
||||||
import { getItem } from "./storage";
|
import { getItem } from "./storage";
|
||||||
import { smallViewport } from "./stores";
|
import { smallViewport } from "./stores";
|
||||||
|
|
||||||
|
|
||||||
|
// Function specific for the Login and CreateAccount modals, where the transition duration is relied upon
|
||||||
|
export function maybeModalFadeIf(...e) {
|
||||||
|
if (e[1] && e[1]._condition)
|
||||||
|
return maybeModalFade(e[0]);
|
||||||
|
else
|
||||||
|
return {
|
||||||
|
delay: 0,
|
||||||
|
duration: e[1].duration,
|
||||||
|
easing: e[1].easing,
|
||||||
|
css: (_t) => ""
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export function maybeModalFade(node) {
|
export function maybeModalFade(node) {
|
||||||
return maybeFade(node, { duration: 175, easing: cubicInOut });
|
return maybeFade(node, { duration: 175, easing: cubicInOut });
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<script>
|
<script>
|
||||||
import { overlayStore } from "../../stores";
|
import { overlayStore } from "../../stores";
|
||||||
import { methods, remoteSignal } from "../../request";
|
import { methods, remoteSignal } from "../../request";
|
||||||
import { maybeModalFade, maybeModalScale } from "../../animations";
|
import Modal from "./Modal.svelte";
|
||||||
|
|
||||||
let communityName = "";
|
let communityName = "";
|
||||||
let createButtonEnabled = true;
|
let createButtonEnabled = true;
|
||||||
|
@ -15,36 +15,20 @@
|
||||||
}
|
}
|
||||||
close();
|
close();
|
||||||
};
|
};
|
||||||
const onKeydown = async (e) => {
|
|
||||||
if (e.code !== "Enter")
|
|
||||||
return;
|
|
||||||
|
|
||||||
await create();
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<Modal {close} enter={create}>
|
||||||
.full-width {
|
<span class="h4" slot="header">Create Community</span>
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<div class="modal-backdrop" transition:maybeModalFade on:click="{ close }" on:keydown="{ onKeydown }">
|
<svelte:fragment slot="content">
|
||||||
<div class="modal" transition:maybeModalScale on:click|stopPropagation>
|
<label class="input-label">
|
||||||
<div class="modal-header">
|
Community Name
|
||||||
<span class="h4">Create Community</span>
|
<input class="input full-width" minlength="1" maxlength="32" bind:value={ communityName } />
|
||||||
</div>
|
</label>
|
||||||
|
</svelte:fragment>
|
||||||
|
|
||||||
<div class="modal-content">
|
<svelte:fragment slot="footer">
|
||||||
<label class="input-label">
|
<button class="button modal-secondary-action" on:click="{ close }">Cancel</button>
|
||||||
Community Name
|
<button class="button button-accent modal-primary-action" on:click="{ create }" disabled="{ !createButtonEnabled }">Create</button>
|
||||||
<input class="input full-width" minlength="1" maxlength="32" bind:value={ communityName } />
|
</svelte:fragment>
|
||||||
</label>
|
</Modal>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="modal-footer">
|
|
||||||
<button class="button modal-secondary-action" on:click="{ close }">Cancel</button>
|
|
||||||
<button class="button button-accent modal-primary-action" on:click="{ create }" disabled="{ !createButtonEnabled }">Create</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<script>
|
<script>
|
||||||
import { overlayStore, OverlayType } from "../../stores";
|
import { overlayStore, OverlayType } from "../../stores";
|
||||||
import { methods, remoteCall } from "../../request";
|
import { methods, remoteCall } from "../../request";
|
||||||
import { maybeModalScale } from "../../animations";
|
import Modal from "./Modal.svelte";
|
||||||
|
|
||||||
let username = "";
|
let username = "";
|
||||||
let password = "";
|
let password = "";
|
||||||
|
@ -30,47 +30,33 @@
|
||||||
overlayStore.push(OverlayType.Login);
|
overlayStore.push(OverlayType.Login);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const onKeydown = async (e) => {
|
|
||||||
if (e.code !== "Enter")
|
|
||||||
return;
|
|
||||||
|
|
||||||
await create();
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.full-width {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.separator {
|
.separator {
|
||||||
margin-bottom: var(--space-md);
|
margin-bottom: var(--space-md);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<div class="modal-backdrop modal-backdrop-opaque" on:keydown="{ onKeydown }">
|
<Modal opaque close={loginInstead} {outroEnd} enter={create}>
|
||||||
<div class="modal" transition:maybeModalScale on:click|stopPropagation on:outroend="{ outroEnd }">
|
<span class="h4" slot="header">Create an Account</span>
|
||||||
<div class="modal-header">
|
|
||||||
<span class="h4">Create an Account</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="modal-content">
|
<svelte:fragment slot="content">
|
||||||
<label class="input-label">
|
<label class="input-label">
|
||||||
Username
|
Username
|
||||||
<input class="input full-width" minlength="1" maxlength="32" bind:value={ username } />
|
<input class="input full-width" minlength="1" maxlength="32" bind:value={ username } />
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<div class="separator" />
|
<div class="separator" />
|
||||||
|
|
||||||
<label class="input-label">
|
<label class="input-label">
|
||||||
Password
|
Password
|
||||||
<input class="input full-width" minlength="8" type="password" bind:value={ password } />
|
<input class="input full-width" minlength="8" type="password" bind:value={ password } />
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</svelte:fragment>
|
||||||
|
|
||||||
<div class="modal-footer">
|
<svelte:fragment slot="footer">
|
||||||
<button class="button modal-secondary-action" on:click="{ loginInstead }">Log in instead</button>
|
<button class="button modal-secondary-action" on:click="{ loginInstead }">Log in instead</button>
|
||||||
<button class="button button-accent modal-primary-action" on:click="{ create }" disabled="{ !buttonsEnabled }">Create</button>
|
<button class="button button-accent modal-primary-action" on:click="{ create }" disabled="{ !buttonsEnabled }">Create</button>
|
||||||
</div>
|
</svelte:fragment>
|
||||||
</div>
|
</Modal>
|
||||||
</div>
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<script>
|
<script>
|
||||||
import { overlayStore } from "../../stores";
|
import { overlayStore } from "../../stores";
|
||||||
import { methods, remoteCall, remoteSignal } from "../../request";
|
import { methods, remoteSignal } from "../../request";
|
||||||
import { maybeModalFade, maybeModalScale } from "../../animations";
|
import Modal from "./Modal.svelte";
|
||||||
|
|
||||||
let channelName = "";
|
let channelName = "";
|
||||||
let createButtonEnabled = true;
|
let createButtonEnabled = true;
|
||||||
|
@ -16,39 +16,25 @@
|
||||||
}
|
}
|
||||||
close();
|
close();
|
||||||
};
|
};
|
||||||
const onKeydown = async (e) => {
|
|
||||||
if (e.code !== "Enter")
|
|
||||||
return;
|
|
||||||
|
|
||||||
await create();
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<Modal {close} enter={create}>
|
||||||
.full-width {
|
<svelte:fragment slot="header">
|
||||||
width: 100%;
|
<span class="h4">Create Channel</span>
|
||||||
}
|
{#if community.id !== -1}
|
||||||
</style>
|
<span class="text-fg-3 text-small">in <span class="text-fg-2">{ community.name }</span></span>
|
||||||
|
{/if}
|
||||||
|
</svelte:fragment>
|
||||||
|
|
||||||
<div class="modal-backdrop" transition:maybeModalFade on:click="{ close }" on:keydown="{ onKeydown }">
|
<svelte:fragment slot="content">
|
||||||
<div class="modal" transition:maybeModalScale on:click|stopPropagation>
|
<label class="input-label">
|
||||||
<div class="modal-header">
|
Channel Name
|
||||||
<span class="h4">Create Channel</span>
|
<input class="input full-width" minlength="1" maxlength="32" bind:value={ channelName } />
|
||||||
{#if community.id !== -1}
|
</label>
|
||||||
<span class="text-fg-3 text-small">in <span class="text-fg-2">{ community.name }</span></span>
|
</svelte:fragment>
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="modal-content">
|
<svelte:fragment slot="footer">
|
||||||
<label class="input-label">
|
<button class="button modal-secondary-action" on:click="{ close }">Cancel</button>
|
||||||
Channel Name
|
<button class="button button-accent modal-primary-action" on:click="{ create }" disabled="{ !createButtonEnabled }">Create</button>
|
||||||
<input class="input full-width" minlength="1" maxlength="32" bind:value={ channelName } />
|
</svelte:fragment>
|
||||||
</label>
|
</Modal>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="modal-footer">
|
|
||||||
<button class="button modal-secondary-action" on:click="{ close }">Cancel</button>
|
|
||||||
<button class="button button-accent modal-primary-action" on:click="{ create }" disabled="{ !createButtonEnabled }">Create</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<script>
|
<script>
|
||||||
import { maybeModalFade, maybeModalScale } from "../../animations";
|
|
||||||
import { overlayStore } from "../../stores";
|
import { overlayStore } from "../../stores";
|
||||||
import { methods, remoteCall, remoteSignal } from "../../request";
|
import { methods, remoteSignal } from "../../request";
|
||||||
|
import Modal from "./Modal.svelte";
|
||||||
|
|
||||||
export let channel;
|
export let channel;
|
||||||
|
|
||||||
|
@ -25,41 +25,27 @@
|
||||||
}
|
}
|
||||||
close();
|
close();
|
||||||
};
|
};
|
||||||
const onKeydown = async (e) => {
|
|
||||||
if (e.code !== "Enter")
|
|
||||||
return;
|
|
||||||
|
|
||||||
await save();
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.full-width {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.delete-button {
|
.delete-button {
|
||||||
color: var(--red-2);
|
color: var(--red-2);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<div class="modal-backdrop" transition:maybeModalFade on:click="{ close }" on:keydown="{ onKeydown }">
|
<Modal {close} enter={save}>
|
||||||
<div class="modal" transition:maybeModalScale on:click|stopPropagation>
|
<span class="h4" slot="header">Edit Channel</span>
|
||||||
<div class="modal-header">
|
|
||||||
<span class="h4">Edit Channel</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="modal-content">
|
<svelte:fragment slot="content">
|
||||||
<label class="input-label">
|
<label class="input-label">
|
||||||
Channel Name
|
Channel Name
|
||||||
<input class="input full-width" minlength="1" maxlength="32" bind:value={ channelName } />
|
<input class="input full-width" minlength="1" maxlength="32" bind:value={ channelName } />
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</svelte:fragment>
|
||||||
|
|
||||||
<div class="modal-footer">
|
<svelte:fragment slot="footer">
|
||||||
<button class="button modal-secondary-action" on:click="{ close }">Cancel</button>
|
<button class="button modal-secondary-action" on:click="{ close }">Cancel</button>
|
||||||
<button class="button modal-secondary-action delete-button" on:click="{ deleteChannel }" disabled="{ !buttonsEnabled }">Delete</button>
|
<button class="button modal-secondary-action delete-button" on:click="{ deleteChannel }" disabled="{ !buttonsEnabled }">Delete</button>
|
||||||
<button class="button button-accent modal-primary-action" on:click="{ save }" disabled="{ !buttonsEnabled }">Save</button>
|
<button class="button button-accent modal-primary-action" on:click="{ save }" disabled="{ !buttonsEnabled }">Save</button>
|
||||||
</div>
|
</svelte:fragment>
|
||||||
</div>
|
</Modal>
|
||||||
</div>
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<script>
|
<script>
|
||||||
import { overlayStore } from "../../stores";
|
import { overlayStore } from "../../stores";
|
||||||
import { methods, remoteCall, remoteSignal } from "../../request";
|
import { methods, remoteSignal } from "../../request";
|
||||||
import { maybeModalFade, maybeModalScale } from "../../animations";
|
import Modal from "./Modal.svelte";
|
||||||
|
|
||||||
export let message;
|
export let message;
|
||||||
|
|
||||||
|
@ -25,41 +25,27 @@
|
||||||
}
|
}
|
||||||
close();
|
close();
|
||||||
};
|
};
|
||||||
const onKeydown = async (e) => {
|
|
||||||
if (e.code !== "Enter")
|
|
||||||
return;
|
|
||||||
|
|
||||||
await save();
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.full-width {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.delete-button {
|
.delete-button {
|
||||||
color: var(--red-2);
|
color: var(--red-2);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<div class="modal-backdrop" transition:maybeModalFade on:click="{ close }" on:keydown="{ onKeydown }">
|
<Modal {close} enter={save}>
|
||||||
<div class="modal" transition:maybeModalScale on:click|stopPropagation>
|
<span class="h4" slot="header">Edit Message</span>
|
||||||
<div class="modal-header">
|
|
||||||
<span class="h4">Edit Message</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="modal-content">
|
<svelte:fragment slot="content">
|
||||||
<label class="input-label">
|
<label class="input-label">
|
||||||
Content
|
Content
|
||||||
<input class="input full-width" minlength="1" bind:value={ messageContent } />
|
<input class="input full-width" minlength="1" bind:value={ messageContent } />
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</svelte:fragment>
|
||||||
|
|
||||||
<div class="modal-footer">
|
<svelte:fragment slot="footer">
|
||||||
<button class="button modal-secondary-action" on:click="{ close }">Cancel</button>
|
<button class="button modal-secondary-action" on:click="{ close }">Cancel</button>
|
||||||
<button class="button modal-secondary-action delete-button" on:click="{ deleteMessage }" disabled="{ !buttonsEnabled }">Delete</button>
|
<button class="button modal-secondary-action delete-button" on:click="{ deleteMessage }" disabled="{ !buttonsEnabled }">Delete</button>
|
||||||
<button class="button button-accent modal-primary-action" on:click="{ save }" disabled="{ !buttonsEnabled }">Save</button>
|
<button class="button button-accent modal-primary-action" on:click="{ save }" disabled="{ !buttonsEnabled }">Save</button>
|
||||||
</div>
|
</svelte:fragment>
|
||||||
</div>
|
</Modal>
|
||||||
</div>
|
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
import { overlayStore, OverlayType } from "../../stores";
|
import { overlayStore, OverlayType } from "../../stores";
|
||||||
import { remoteCall } from "../../request";
|
import { remoteCall } from "../../request";
|
||||||
import { authWithToken } from "../../auth";
|
import { authWithToken } from "../../auth";
|
||||||
import { maybeModalScale } from "../../animations";
|
|
||||||
import { methods } from "../../request";
|
import { methods } from "../../request";
|
||||||
|
import Modal from "./Modal.svelte";
|
||||||
|
|
||||||
let username = "";
|
let username = "";
|
||||||
let password = "";
|
let password = "";
|
||||||
|
@ -35,47 +35,33 @@
|
||||||
overlayStore.push(OverlayType.CreateAccount);
|
overlayStore.push(OverlayType.CreateAccount);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const onKeydown = async (e) => {
|
|
||||||
if (e.code !== "Enter")
|
|
||||||
return;
|
|
||||||
|
|
||||||
await login();
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.full-width {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.separator {
|
.separator {
|
||||||
margin-bottom: var(--space-md);
|
margin-bottom: var(--space-md);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<div class="modal-backdrop modal-backdrop-opaque" on:keydown="{ onKeydown }">
|
<Modal opaque close={createAccountInstead} {outroEnd} enter={login}>
|
||||||
<div class="modal" transition:maybeModalScale on:click|stopPropagation on:outroend="{ outroEnd }">
|
<span class="h4" slot="header">Welcome back!</span>
|
||||||
<div class="modal-header">
|
|
||||||
<span class="h4">Welcome back!</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="modal-content">
|
<svelte:fragment slot="content">
|
||||||
<label class="input-label">
|
<label class="input-label">
|
||||||
Username
|
Username
|
||||||
<input class="input full-width" minlength="1" maxlength="32" bind:value={ username } />
|
<input class="input full-width" minlength="1" maxlength="32" bind:value={ username } />
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<div class="separator" />
|
<div class="separator" />
|
||||||
|
|
||||||
<label class="input-label">
|
<label class="input-label">
|
||||||
Password
|
Password
|
||||||
<input class="input full-width" minlength="8" type="password" bind:value={ password } />
|
<input class="input full-width" minlength="8" type="password" bind:value={ password } />
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</svelte:fragment>
|
||||||
|
|
||||||
<div class="modal-footer">
|
<svelte:fragment slot="footer">
|
||||||
<button class="button modal-secondary-action" on:click="{ createAccountInstead }">Create an account instead</button>
|
<button class="button modal-secondary-action" on:click="{ createAccountInstead }">Create an account instead</button>
|
||||||
<button class="button button-accent modal-primary-action" on:click="{ login }" disabled="{ !buttonsEnabled }">Log In</button>
|
<button class="button button-accent modal-primary-action" on:click="{ login }" disabled="{ !buttonsEnabled }">Log In</button>
|
||||||
</div>
|
</svelte:fragment>
|
||||||
</div>
|
</Modal>
|
||||||
</div>
|
|
||||||
|
|
57
frontend/src/components/overlays/Modal.svelte
Normal file
57
frontend/src/components/overlays/Modal.svelte
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
<script>
|
||||||
|
import { onMount } from "svelte";
|
||||||
|
import { maybeModalFade, maybeModalFadeIf, maybeModalScale } from "../../animations";
|
||||||
|
|
||||||
|
export let close = () => {};
|
||||||
|
export let enter = () => {};
|
||||||
|
export let outroEnd = () => {};
|
||||||
|
export let className = "";
|
||||||
|
export let opaque = false;
|
||||||
|
let modal;
|
||||||
|
let blur = false;
|
||||||
|
|
||||||
|
const onKeydown = ({ code }) => {
|
||||||
|
if (code === "Enter") {
|
||||||
|
enter();
|
||||||
|
} else if (code === "Escape") {
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const backdropIntroEnd = () => {
|
||||||
|
blur = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const backdropOutroStart = () => {
|
||||||
|
blur = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
modal.focus();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
|
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||||
|
<!-- svelte-ignore a11y-no-noninteractive-element-interactions -->
|
||||||
|
<div class="modal-backdrop" class:modal-backdrop-opaque={opaque} class:blur={blur} transition:maybeModalFadeIf="{{ _condition: !opaque }}" on:click="{ close }" on:keydown="{ onKeydown }" on:introend={backdropIntroEnd} on:outrostart={backdropOutroStart}>
|
||||||
|
<div bind:this={modal} role="alertdialog" tabindex="-1" aria-modal="true" class={className + " modal"} transition:maybeModalScale on:click|stopPropagation on:outroend={outroEnd}>
|
||||||
|
{#if $$slots.header}
|
||||||
|
<div class="modal-header">
|
||||||
|
<slot name="header" />
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
{#if $$slots.content}
|
||||||
|
<div class="modal-content">
|
||||||
|
<slot name="content" />
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
{#if $$slots.footer}
|
||||||
|
<div class="modal-footer">
|
||||||
|
<slot name="footer" />
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -1,5 +1,5 @@
|
||||||
<script>
|
<script>
|
||||||
import { maybeModalFade, maybeModalScale } from "../../animations";
|
import Modal from "./Modal.svelte";
|
||||||
|
|
||||||
export let onSubmit = async () => {};
|
export let onSubmit = async () => {};
|
||||||
export let onClose = async () => {};
|
export let onClose = async () => {};
|
||||||
|
@ -19,36 +19,18 @@
|
||||||
await onSubmit(userInput);
|
await onSubmit(userInput);
|
||||||
closePrompt();
|
closePrompt();
|
||||||
};
|
};
|
||||||
const onKeydown = async (e) => {
|
|
||||||
if (e.code !== "Enter")
|
|
||||||
return;
|
|
||||||
|
|
||||||
await save();
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<Modal {closePrompt} enter={save}>
|
||||||
.full-width {
|
<span class="h4" slot="header">{ heading }</span>
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<div class="modal-backdrop" transition:maybeModalFade on:click="{ closePrompt }" on:keydown="{ onKeydown }">
|
<label class="input-label" slot="content">
|
||||||
<div class="modal" transition:maybeModalScale on:click|stopPropagation>
|
{ valueName }
|
||||||
<div class="modal-header">
|
<input class="input full-width" bind:value={ userInput } />
|
||||||
<span class="h4">{ heading }</span>
|
</label>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="modal-content">
|
<svelte:fragment slot="footer">
|
||||||
<label class="input-label">
|
<button class="button modal-secondary-action" on:click="{ closePrompt }">Cancel</button>
|
||||||
{ valueName }
|
<button class="button button-accent modal-primary-action" on:click="{ save }" disabled="{ !buttonsEnabled }">Submit</button>
|
||||||
<input class="input full-width" bind:value={ userInput } />
|
</svelte:fragment>
|
||||||
</label>
|
</Modal>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="modal-footer">
|
|
||||||
<button class="button modal-secondary-action" on:click="{ closePrompt }">Cancel</button>
|
|
||||||
<button class="button button-accent modal-primary-action" on:click="{ save }" disabled="{ !buttonsEnabled }">Submit</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
|
@ -2,12 +2,12 @@
|
||||||
import { overlayStore, userInfoStore, smallViewport, theme, doAnimations, OverlayType, sendTypingUpdatesItemStore } from "../../stores";
|
import { overlayStore, userInfoStore, smallViewport, theme, doAnimations, OverlayType, sendTypingUpdatesItemStore } from "../../stores";
|
||||||
import { logOut } from "../../auth";
|
import { logOut } from "../../auth";
|
||||||
import { maybeModalFade, maybeModalScale } from "../../animations";
|
import { maybeModalFade, maybeModalScale } from "../../animations";
|
||||||
import request, { methods, remoteBlobUpload, remoteCall } from "../../request";
|
import request, { methods, remoteBlobUpload } from "../../request";
|
||||||
import { apiRoute, getItem } from "../../storage";
|
import { apiRoute, getItem } from "../../storage";
|
||||||
import UserView from "../UserView.svelte";
|
import UserView from "../UserView.svelte";
|
||||||
import ChipBar from "../ChipBar.svelte";
|
import ChipBar from "../ChipBar.svelte";
|
||||||
import Switch from "../Switch.svelte";
|
|
||||||
import StoredSwitch from "../StoredSwitch.svelte";
|
import StoredSwitch from "../StoredSwitch.svelte";
|
||||||
|
import Modal from "./Modal.svelte";
|
||||||
|
|
||||||
export let close = () => {};
|
export let close = () => {};
|
||||||
let avatarFileInput;
|
let avatarFileInput;
|
||||||
|
@ -76,12 +76,7 @@
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
|
||||||
<style>
|
<style>
|
||||||
.full-width {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.separator {
|
.separator {
|
||||||
margin-bottom: var(--space-sm);
|
margin-bottom: var(--space-sm);
|
||||||
}
|
}
|
||||||
|
@ -128,7 +123,7 @@
|
||||||
grid-template-columns: repeat(2, 1fr);
|
grid-template-columns: repeat(2, 1fr);
|
||||||
}
|
}
|
||||||
|
|
||||||
.large-settings {
|
:global(.large-settings) {
|
||||||
width: 600px;
|
width: 600px;
|
||||||
min-height: 425px;
|
min-height: 425px;
|
||||||
padding-bottom: var(--space-xs);
|
padding-bottom: var(--space-xs);
|
||||||
|
@ -138,77 +133,72 @@
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.settings-modal {
|
:global(.settings-modal) {
|
||||||
background-color: var(--background-color-1);
|
background-color: var(--background-color-1) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.settings-modal .modal-header {
|
:global(.settings-modal .modal-header) {
|
||||||
padding-bottom: var(--space-xxs);
|
padding-bottom: var(--space-xxs);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
<Modal {close} className={`settings-modal ${$smallViewport ? "" : "large-settings"}`}>
|
||||||
<div class="modal-backdrop" transition:maybeModalFade on:click="{ close }">
|
<span class="h4" slot="header">Settings</span>
|
||||||
<div class="modal settings-modal" class:large-settings="{ !$smallViewport }" transition:maybeModalScale on:click|stopPropagation>
|
|
||||||
<div class="modal-header">
|
|
||||||
<span class="h4">Settings</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="modal-content">
|
<svelte:fragment slot="content">
|
||||||
<ChipBar selectedOptionId="ACCOUNT" onSelect={ (tab) => selectedTab = tab } options={[
|
<ChipBar selectedOptionId="ACCOUNT" onSelect={ (tab) => selectedTab = tab } options={[
|
||||||
{ id: "ACCOUNT", text: "Account", icon: "person" },
|
{ id: "ACCOUNT", text: "Account", icon: "person" },
|
||||||
{ id: "PRIVACY", text: "Privacy", icon: "lock" },
|
{ id: "PRIVACY", text: "Privacy", icon: "lock" },
|
||||||
{ id: "APPEARANCE", text: "Appearance", icon: "palette" },
|
{ id: "APPEARANCE", text: "Appearance", icon: "palette" },
|
||||||
]}></ChipBar>
|
]}></ChipBar>
|
||||||
|
|
||||||
|
<div class="separator" />
|
||||||
|
|
||||||
|
{#if selectedTab === "ACCOUNT"}
|
||||||
|
<div class="settings-card full-width">
|
||||||
|
<UserView user={$userInfoStore}></UserView>
|
||||||
|
<input type="file" style="display: none;" accept="image/png, image/jpeg, image/webp" name="avatar-upload" multiple={false} bind:this={avatarFileInput} on:change={onAvatarFileChange}>
|
||||||
|
<div class="left-auto">
|
||||||
|
<button class="button" on:click="{ openAvatarInput }">Update Avatar</button>
|
||||||
|
<button class="button button-danger" on:click="{ doLogout }">Log Out</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{:else if selectedTab == "PRIVACY"}
|
||||||
|
<div class="switch-option-card full-width">
|
||||||
|
<div class="info">
|
||||||
|
<span class="info-heading">Let others know when I'm typing</span>
|
||||||
|
<span class="text-fg-3 text-small">If this is enabled, other users will see an indicator while you're typing a message.</span>
|
||||||
|
</div>
|
||||||
|
<div class="option-switch">
|
||||||
|
<StoredSwitch store={ sendTypingUpdatesItemStore } />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="switch-option-card full-width">
|
||||||
|
<div class="info">
|
||||||
|
<span class="info-heading">Make Waffle work</span>
|
||||||
|
<span class="text-fg-3 text-small">Waffle needs to store data such as your messages, created channels, your username, your profile picture and more in order to work. If you'd like to stop this, you can delete your account.</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{:else if selectedTab === "APPEARANCE"}
|
||||||
|
<span class="input-label">Theme</span>
|
||||||
|
<div class="horizontal-selections">
|
||||||
|
<button class="button selection-option full-width selected" class:selected="{ $theme === "dark" }" on:click="{ () => theme.set('dark') }">Dark</button>
|
||||||
|
<button class="button selection-option full-width" class:selected="{ $theme === "light" }" on:click="{ () => theme.set('light') }">Light</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="separator" />
|
<div class="separator" />
|
||||||
|
|
||||||
{#if selectedTab === "ACCOUNT"}
|
<div class="switch-option-card full-width">
|
||||||
<div class="settings-card full-width">
|
<div class="info">
|
||||||
<UserView user={$userInfoStore}></UserView>
|
<span class="info-heading">Reduce animations</span>
|
||||||
<input type="file" style="display: none;" accept="image/png, image/jpeg, image/webp" name="avatar-upload" multiple={false} bind:this={avatarFileInput} on:change={onAvatarFileChange}>
|
<span class="text-fg-3 text-small">Reduce the amount of animations and visual effects.</span>
|
||||||
<div class="left-auto">
|
|
||||||
<button class="button" on:click="{ openAvatarInput }">Update Avatar</button>
|
|
||||||
<button class="button button-danger" on:click="{ doLogout }">Log Out</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
{:else if selectedTab == "PRIVACY"}
|
<div class="option-switch">
|
||||||
<div class="switch-option-card full-width">
|
<StoredSwitch store={ doAnimations } inverted={ true } />
|
||||||
<div class="info">
|
|
||||||
<span class="info-heading">Let others know when I'm typing</span>
|
|
||||||
<span class="text-fg-3 text-small">If this is enabled, other users will see an indicator while you're typing a message.</span>
|
|
||||||
</div>
|
|
||||||
<div class="option-switch">
|
|
||||||
<StoredSwitch store={ sendTypingUpdatesItemStore } />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="switch-option-card full-width">
|
</div>
|
||||||
<div class="info">
|
{:else}
|
||||||
<span class="info-heading">Make Waffle work</span>
|
<span>Page not found: { selectedTab }</span>
|
||||||
<span class="text-fg-3 text-small">Waffle needs to store data such as your messages, created channels, your username, your profile picture and more in order to work. If you'd like to stop this, you can delete your account.</span>
|
{/if}
|
||||||
</div>
|
</svelte:fragment>
|
||||||
</div>
|
</Modal>
|
||||||
{:else if selectedTab === "APPEARANCE"}
|
|
||||||
<span class="input-label">Theme</span>
|
|
||||||
<div class="horizontal-selections">
|
|
||||||
<button class="button selection-option full-width selected" class:selected="{ $theme === "dark" }" on:click="{ () => theme.set('dark') }">Dark</button>
|
|
||||||
<button class="button selection-option full-width" class:selected="{ $theme === "light" }" on:click="{ () => theme.set('light') }">Light</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="separator" />
|
|
||||||
|
|
||||||
<div class="switch-option-card full-width">
|
|
||||||
<div class="info">
|
|
||||||
<span class="info-heading">Reduce animations</span>
|
|
||||||
<span class="text-fg-3 text-small">Reduce the amount of animations and visual effects.</span>
|
|
||||||
</div>
|
|
||||||
<div class="option-switch">
|
|
||||||
<StoredSwitch store={ doAnimations } inverted={ true } />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{:else}
|
|
||||||
<span>Page not found: { selectedTab }</span>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
|
@ -54,13 +54,13 @@
|
||||||
/* top-level */
|
/* top-level */
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
--purple-1: hsl(273, 67%, 53%);
|
--purple-1: hsl(266, 63%, 64%);
|
||||||
--blue-1: hsl(200, 78%, 50%);
|
--blue-1: hsl(200, 78%, 50%);
|
||||||
--green-1: hsl(140, 78%, 50%);
|
--green-1: hsl(140, 78%, 50%);
|
||||||
--yellow-1: hsl(50, 78%, 50%);
|
--yellow-1: hsl(50, 78%, 50%);
|
||||||
--red-1: hsl(2, 78%, 65%);
|
--red-1: hsl(2, 78%, 65%);
|
||||||
|
|
||||||
--purple-2: hsl(273, 64%, 48%);
|
--purple-2: hsl(266, 62%, 58%);
|
||||||
--blue-2: hsl(200, 78%, 45%);
|
--blue-2: hsl(200, 78%, 45%);
|
||||||
--green-2: hsl(140, 78%, 40%);
|
--green-2: hsl(140, 78%, 40%);
|
||||||
--yellow-2: hsl(50, 78%, 60%);
|
--yellow-2: hsl(50, 78%, 60%);
|
||||||
|
@ -222,10 +222,13 @@ body {
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
z-index: 15;
|
z-index: 15;
|
||||||
background-color: rgba(0, 0, 0, 0.4);
|
background-color: rgba(0, 0, 0, 0.4);
|
||||||
backdrop-filter: blur(1.5px);
|
|
||||||
contain: strict;
|
contain: strict;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.modal-backdrop.blur {
|
||||||
|
backdrop-filter: blur(1.5px);
|
||||||
|
}
|
||||||
|
|
||||||
.modal-backdrop-opaque {
|
.modal-backdrop-opaque {
|
||||||
background-color: var(--background-color-1);
|
background-color: var(--background-color-1);
|
||||||
backdrop-filter: unset;
|
backdrop-filter: unset;
|
||||||
|
@ -284,7 +287,6 @@ body {
|
||||||
.modal-backdrop {
|
.modal-backdrop {
|
||||||
align-items: flex-end;
|
align-items: flex-end;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
backdrop-filter: unset;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-backdrop-opaque {
|
.modal-backdrop-opaque {
|
||||||
|
@ -327,8 +329,10 @@ body {
|
||||||
background: none;
|
background: none;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
border: none;
|
border: none;
|
||||||
padding: 0.7em;
|
padding: 0.85em;
|
||||||
border-radius: 1em;
|
padding-top: 0.65em;
|
||||||
|
padding-bottom: 0.65em;
|
||||||
|
border-radius: 9999px;
|
||||||
font: inherit;
|
font: inherit;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
font-weight: 550;
|
font-weight: 550;
|
||||||
|
@ -588,6 +592,10 @@ body {
|
||||||
border-bottom-left-radius: var(--radius-mdplus);
|
border-bottom-left-radius: var(--radius-mdplus);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.full-width {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*! the tweaks below are heavily based on modern-normalize v1.1.0 | MIT License | https://github.com/sindresorhus/modern-normalize */
|
/*! the tweaks below are heavily based on modern-normalize v1.1.0 | MIT License | https://github.com/sindresorhus/modern-normalize */
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue