feat: implement theme changing
quick settings are now properly implemented
This commit is contained in:
		@@ -101,6 +101,8 @@
 | 
			
		||||
     text-transform: lowercase;
 | 
			
		||||
 }
 | 
			
		||||
 | 
			
		||||
 .top { height: 18px; }
 | 
			
		||||
 | 
			
		||||
 .channel.local {
 | 
			
		||||
     background-color: var(--cyan);
 | 
			
		||||
 }
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										146
									
								
								src/lib/QuickSetting.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										146
									
								
								src/lib/QuickSetting.svelte
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,146 @@
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
 import { FontAwesomeIcon } from '@fortawesome/svelte-fontawesome';
 | 
			
		||||
 import type { IconDefinition } from '@fortawesome/free-solid-svg-icons'
 | 
			
		||||
 | 
			
		||||
 import { crossfade, fade } from 'svelte/transition';
 | 
			
		||||
 import { expoInOut } from 'svelte/easing';
 | 
			
		||||
 | 
			
		||||
 const [send, receive] = crossfade({
 | 
			
		||||
     duration: 350,
 | 
			
		||||
     easing: expoInOut
 | 
			
		||||
 });
 | 
			
		||||
 | 
			
		||||
 import { createEventDispatcher } from "svelte";
 | 
			
		||||
 const dispatch = createEventDispatcher();
 | 
			
		||||
 | 
			
		||||
 export let enabled: boolean = false;
 | 
			
		||||
 export let icon: IconDefinition | undefined = undefined;
 | 
			
		||||
 export let chevron: IconDefinition | undefined = undefined;
 | 
			
		||||
 export let title: string;
 | 
			
		||||
 export let subtitle: string = "";
 | 
			
		||||
 | 
			
		||||
 export let showMenu: boolean = false;
 | 
			
		||||
 export let menu: {
 | 
			
		||||
     title: string,
 | 
			
		||||
     items?: {
 | 
			
		||||
	 value: string,
 | 
			
		||||
	 enabled?: boolean,
 | 
			
		||||
     }[]
 | 
			
		||||
 } = { title: "Menu" };
 | 
			
		||||
 | 
			
		||||
 export let column: number | undefined = undefined, row: number | undefined = undefined;
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
{#if showMenu}
 | 
			
		||||
    <div class="modal-background clickable" transition:fade="{{easing: expoInOut}}"
 | 
			
		||||
	 on:click={() => showMenu = false} />
 | 
			
		||||
    <div class="menu" in:send="{{key: 0}}" out:receive="{{key: 0}}">
 | 
			
		||||
	<h1 class="no-select">{menu.title}</h1>
 | 
			
		||||
	<div class="items d-flex col">
 | 
			
		||||
	    {#each menu.items || [] as item}
 | 
			
		||||
		<button class="item mb-8 radius-24 border-0 px-24 py-24"
 | 
			
		||||
			class:enabled="{item.enabled}"
 | 
			
		||||
			on:click={() => dispatch("menu:click", item.value)}
 | 
			
		||||
		    >{item.value}</button>
 | 
			
		||||
	    {/each}
 | 
			
		||||
	</div>
 | 
			
		||||
    </div>
 | 
			
		||||
{:else}
 | 
			
		||||
    <button in:send="{{key: 0}}" out:receive="{{key: 0}}"
 | 
			
		||||
	    style:grid-row-start="{row}"
 | 
			
		||||
	    style:grid-column-start="{column}"
 | 
			
		||||
	    class:enabled="{enabled}"
 | 
			
		||||
	    class="border-0 ma-8 radius-32 d-flex pa-8 no-select"
 | 
			
		||||
	    on:click>
 | 
			
		||||
	<div class="button-icon px-8">
 | 
			
		||||
	    {#if icon}
 | 
			
		||||
		<FontAwesomeIcon icon="{icon}" />
 | 
			
		||||
	    {/if}
 | 
			
		||||
	</div>
 | 
			
		||||
	<div class="button-text d-flex col grow-1">
 | 
			
		||||
	    <div class="title mb-2">
 | 
			
		||||
		{title}
 | 
			
		||||
	    </div>
 | 
			
		||||
	    <div class="subtitle overflow-hidden">
 | 
			
		||||
		{subtitle}
 | 
			
		||||
	    </div>
 | 
			
		||||
	</div>
 | 
			
		||||
	<div class="button-icon px-8">
 | 
			
		||||
	    {#if chevron}
 | 
			
		||||
		<FontAwesomeIcon icon="{chevron}" />
 | 
			
		||||
	    {/if}
 | 
			
		||||
	</div>
 | 
			
		||||
    </button>
 | 
			
		||||
{/if}
 | 
			
		||||
 | 
			
		||||
<style lang="scss">
 | 
			
		||||
 .hide {
 | 
			
		||||
     visibility: hidden;
 | 
			
		||||
 }
 | 
			
		||||
 | 
			
		||||
 button {
 | 
			
		||||
     height: auto;
 | 
			
		||||
     align-items: center;
 | 
			
		||||
     text-align: start;
 | 
			
		||||
     /* padding-left: 24px; */
 | 
			
		||||
     font-size: 1.2em;
 | 
			
		||||
     transition: background-color;
 | 
			
		||||
     transition-duration: 250ms;
 | 
			
		||||
     font-size: calc(15px + 0.390625vw);
 | 
			
		||||
 }
 | 
			
		||||
 | 
			
		||||
 button.enabled {
 | 
			
		||||
     background: var(--base-600);
 | 
			
		||||
     color: var(--base-700);
 | 
			
		||||
 }
 | 
			
		||||
 button:not(.enabled) {
 | 
			
		||||
     background: var(--base-800);
 | 
			
		||||
 }
 | 
			
		||||
 | 
			
		||||
 .button-text {
 | 
			
		||||
     min-width: 0;
 | 
			
		||||
 }
 | 
			
		||||
 | 
			
		||||
 button .title {
 | 
			
		||||
     font-weight: bold;
 | 
			
		||||
     font-size: .9em;
 | 
			
		||||
     text-overflow: ellipsis;
 | 
			
		||||
     white-space: nowrap;
 | 
			
		||||
 }
 | 
			
		||||
 button .subtitle {
 | 
			
		||||
     font-size: .85em;
 | 
			
		||||
     font-weight: 300;
 | 
			
		||||
     text-overflow: ellipsis;
 | 
			
		||||
     white-space: nowrap;
 | 
			
		||||
 }
 | 
			
		||||
 | 
			
		||||
 .menu {
 | 
			
		||||
     position: absolute;
 | 
			
		||||
     left: 50%;
 | 
			
		||||
     top: 50%;
 | 
			
		||||
     width: calc(100vw - 4em);
 | 
			
		||||
     max-width: 32em;
 | 
			
		||||
     max-height: calc(100vh - 4em);
 | 
			
		||||
     overflow: auto;
 | 
			
		||||
     transform: translate(-50%,-50%);
 | 
			
		||||
     padding: 1em;
 | 
			
		||||
     border-radius: 12px;
 | 
			
		||||
     background: var(--base-700);
 | 
			
		||||
     z-index: 300;
 | 
			
		||||
 }
 | 
			
		||||
 | 
			
		||||
 .modal-background {
 | 
			
		||||
     position: fixed;
 | 
			
		||||
     top: 0;
 | 
			
		||||
     left: 0;
 | 
			
		||||
     width: 100%;
 | 
			
		||||
     height: 100%;
 | 
			
		||||
     background: rgba(0,0,0,0.3);
 | 
			
		||||
     z-index: 300;
 | 
			
		||||
 }
 | 
			
		||||
 | 
			
		||||
 .items {
 | 
			
		||||
 | 
			
		||||
 }
 | 
			
		||||
 | 
			
		||||
</style>
 | 
			
		||||
							
								
								
									
										79
									
								
								src/lib/QuickSettings.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								src/lib/QuickSettings.svelte
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,79 @@
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
 import QuickSetting from "./QuickSetting.svelte";
 | 
			
		||||
 import { globalMode, themes, theme, type Theme } from "./settings";
 | 
			
		||||
 import { faEarthEurope, faPaintRoller, faChevronRight } from '@fortawesome/free-solid-svg-icons';
 | 
			
		||||
 import type { ComponentProps } from "svelte";
 | 
			
		||||
 import { writable } from "svelte/store";
 | 
			
		||||
 | 
			
		||||
 export let rows = 2;
 | 
			
		||||
 export let cols = 2;
 | 
			
		||||
 | 
			
		||||
 let showMenus = writable(Array.from(Array(rows*cols).keys()).map(() => false));
 | 
			
		||||
 | 
			
		||||
 // TODO: figure out how deep object reactivity works, this doesn't feel clean
 | 
			
		||||
 let settings: (ComponentProps<QuickSetting> & Record<string, any>)[];
 | 
			
		||||
 $: {
 | 
			
		||||
     settings = [
 | 
			
		||||
	 {
 | 
			
		||||
	     title: "Global Mode",
 | 
			
		||||
	     subtitle: $globalMode ? 'Enabled' : 'Disabled',
 | 
			
		||||
	     enabled: $globalMode,
 | 
			
		||||
	     icon: faEarthEurope,
 | 
			
		||||
	     click: () => $globalMode = !$globalMode,
 | 
			
		||||
	 },
 | 
			
		||||
	 {
 | 
			
		||||
	     title: "Theme",
 | 
			
		||||
	     subtitle: $theme,
 | 
			
		||||
	     enabled: true,
 | 
			
		||||
	     icon: faPaintRoller,
 | 
			
		||||
	     chevron: faChevronRight,
 | 
			
		||||
	     showMenu: $showMenus[1],
 | 
			
		||||
	     click: () => $showMenus[1] = !$showMenus[1],
 | 
			
		||||
	     menu: {
 | 
			
		||||
		 title: "Theme",
 | 
			
		||||
		 items: themes.map(t => ({
 | 
			
		||||
		     value: t,
 | 
			
		||||
		     enabled: $theme === t,
 | 
			
		||||
		 }))
 | 
			
		||||
	     },
 | 
			
		||||
	     menuClick: (theme: CustomEvent<Theme>) => $theme = theme.detail
 | 
			
		||||
	 },
 | 
			
		||||
	 {
 | 
			
		||||
	     title: "",
 | 
			
		||||
	     subtitle: '',
 | 
			
		||||
	 },
 | 
			
		||||
	 {
 | 
			
		||||
	     title: "",
 | 
			
		||||
	     subtitle: '',
 | 
			
		||||
	 }
 | 
			
		||||
     ];
 | 
			
		||||
 }
 | 
			
		||||
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<div class="quick-settings h-100 w-100"
 | 
			
		||||
     style:--rows="{rows}"
 | 
			
		||||
     style:--cols="{cols}">
 | 
			
		||||
    {#each settings as setting, i}
 | 
			
		||||
	<QuickSetting
 | 
			
		||||
	    row="{Math.floor(i / rows) + 1}"
 | 
			
		||||
	    column="{(i % rows) + 1}"
 | 
			
		||||
	    enabled="{setting.enabled}"
 | 
			
		||||
	    icon="{setting.icon}"
 | 
			
		||||
	    chevron="{setting.chevron}"
 | 
			
		||||
	    title="{setting.title}"
 | 
			
		||||
	    bind:showMenu="{$showMenus[i]}"
 | 
			
		||||
	    menu="{setting.menu}"
 | 
			
		||||
	    subtitle="{setting.subtitle}"
 | 
			
		||||
	    on:click="{setting.click}"
 | 
			
		||||
	    on:menu:click="{setting.menuClick}" />
 | 
			
		||||
    {/each}
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
<style lang="scss">
 | 
			
		||||
 .quick-settings {
 | 
			
		||||
     display: grid;
 | 
			
		||||
     grid-template-rows: repeat(var(--rows), calc(100% / var(--rows)));
 | 
			
		||||
     grid-template-columns: repeat(var(--cols), calc(100% / var(--cols)));
 | 
			
		||||
 }
 | 
			
		||||
</style>
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
 import Header from './Header.svelte';
 | 
			
		||||
 import QuickSettings from './QuickSettings.svelte';
 | 
			
		||||
 import { globalMode, theme } from './settings';
 | 
			
		||||
 import { mentions } from './chat';
 | 
			
		||||
 import MessageView from './MessageView.svelte';
 | 
			
		||||
@@ -63,42 +64,11 @@
 | 
			
		||||
    <div class="contents">
 | 
			
		||||
	<div class="settings">
 | 
			
		||||
	    <div class="quick-settings">
 | 
			
		||||
		<div class="button" on:click="{globalModeClick}"
 | 
			
		||||
		     class:enabled="{$globalMode}">
 | 
			
		||||
		    <div class="button-icon">
 | 
			
		||||
			<FontAwesomeIcon icon="{faEarthEurope}" />
 | 
			
		||||
		    </div>
 | 
			
		||||
		    <div class="button-text">
 | 
			
		||||
			<div class="title">
 | 
			
		||||
			    Global Mode
 | 
			
		||||
			</div>
 | 
			
		||||
			<div class="subtitle">
 | 
			
		||||
			    {$globalMode ? 'Enabled' : 'Disabled'}
 | 
			
		||||
			</div>
 | 
			
		||||
		    </div>
 | 
			
		||||
		</div>
 | 
			
		||||
 | 
			
		||||
		<div class="button">
 | 
			
		||||
		    <div class="button-icon">
 | 
			
		||||
			<FontAwesomeIcon icon="{faPaintRoller}" />
 | 
			
		||||
		    </div>
 | 
			
		||||
		    <div class="button-text">
 | 
			
		||||
			<div class="title">
 | 
			
		||||
			    Theme
 | 
			
		||||
			</div>
 | 
			
		||||
			<div class="subtitle">
 | 
			
		||||
			    {$theme}
 | 
			
		||||
			</div>
 | 
			
		||||
		    </div>
 | 
			
		||||
		    <div class="button-icon">
 | 
			
		||||
			<FontAwesomeIcon icon="{faChevronRight}" />
 | 
			
		||||
		    </div>
 | 
			
		||||
		</div>
 | 
			
		||||
 | 
			
		||||
		<div class="button" />
 | 
			
		||||
		<div class="button" />
 | 
			
		||||
		<QuickSettings />
 | 
			
		||||
	    </div>
 | 
			
		||||
 | 
			
		||||
	    <div class="spacer"/>
 | 
			
		||||
 | 
			
		||||
	    <div class="options-bar">
 | 
			
		||||
		<div class="button enabled">Edit</div>
 | 
			
		||||
		<div class="button enabled">Theme</div>
 | 
			
		||||
@@ -178,9 +148,9 @@
 | 
			
		||||
 }
 | 
			
		||||
 | 
			
		||||
 .quick-settings {
 | 
			
		||||
     display: grid;
 | 
			
		||||
     grid-template-rows: repeat(2, 1fr);
 | 
			
		||||
     grid-template-columns: repeat(2, 50%);
 | 
			
		||||
     /* display: grid; */
 | 
			
		||||
     /* grid-template-rows: repeat(2, 1fr); */
 | 
			
		||||
     /* grid-template-columns: repeat(2, 50%); */
 | 
			
		||||
     height: 35%;
 | 
			
		||||
 | 
			
		||||
     :global(.container:not(.mobile)) & {
 | 
			
		||||
 
 | 
			
		||||
@@ -2,6 +2,7 @@ import { writable } from 'svelte/store';
 | 
			
		||||
 | 
			
		||||
export const globalMode = writable(true);
 | 
			
		||||
 | 
			
		||||
export type Theme = 'solarized-light' | 'solarized-dark';
 | 
			
		||||
export const themes = ['solarized-light', 'solarized-dark'] as const;
 | 
			
		||||
export type Theme = (typeof themes)[number];
 | 
			
		||||
 | 
			
		||||
export const theme = writable<Theme>('solarized-light');
 | 
			
		||||
 
 | 
			
		||||
@@ -20,6 +20,8 @@
 | 
			
		||||
 | 
			
		||||
 import { clickOutside } from "$lib/ui/clickOutside";
 | 
			
		||||
 | 
			
		||||
 import { theme } from "$lib/settings";
 | 
			
		||||
 | 
			
		||||
 import { offset, flip, shift } from "@floating-ui/dom";
 | 
			
		||||
 import { createFloatingActions } from "svelte-floating-ui";
 | 
			
		||||
 import UserSettings from "$lib/UserSettings.svelte";
 | 
			
		||||
@@ -140,8 +142,19 @@
 | 
			
		||||
	 }
 | 
			
		||||
     }
 | 
			
		||||
 }
 | 
			
		||||
 | 
			
		||||
 function updateTheme(node, theme) {
 | 
			
		||||
     node.className = theme;
 | 
			
		||||
     return {
 | 
			
		||||
	 update: (theme) => {
 | 
			
		||||
	     node.className = theme;
 | 
			
		||||
	 }
 | 
			
		||||
     }
 | 
			
		||||
 }
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<svelte:body use:updateTheme="{$theme}" />
 | 
			
		||||
 | 
			
		||||
<div class="container hide-scrollbar snap-y h-100vh"
 | 
			
		||||
     class:mobile="{mobile}"
 | 
			
		||||
     bind:this="{container}" on:scroll="{onScroll}"
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user