adds dark mode
This commit is contained in:
@@ -3,7 +3,7 @@
|
|||||||
@import 'tailwindcss/components';
|
@import 'tailwindcss/components';
|
||||||
|
|
||||||
.btn-primary {
|
.btn-primary {
|
||||||
@apply h-10 text-white rounded-lg px-4 py-2 bg-primary font-semibold transition-all hover:bg-gray-900 focus:ring-4 ring-gray-400
|
@apply h-10 text-white rounded-lg px-4 py-2 bg-primary font-semibold transition-all hover:bg-primary-hover focus:ring-4 ring-input-border-focus
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-large {
|
.btn-large {
|
||||||
@@ -11,7 +11,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.btn-secondary {
|
.btn-secondary {
|
||||||
@apply rounded-lg px-4 py-2 border border-primary font-medium transition-all hover:bg-gray-100 focus:ring-4 ring-gray-200
|
@apply rounded-lg px-4 py-2 border border-input-border-focus font-medium transition-all hover:bg-secondary-hover focus:ring-4 ring-input-border-focus
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-danger {
|
.btn-danger {
|
||||||
@@ -22,6 +22,10 @@
|
|||||||
@apply bg-gray-500 hover:bg-gray-500
|
@apply bg-gray-500 hover:bg-gray-500
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.btn-icon {
|
||||||
|
@apply rounded-full aspect-square h-7 hover:bg-secondary-hover focus:ring-4 ring-input-border-focus transition-colors;
|
||||||
|
}
|
||||||
|
|
||||||
.tab {
|
.tab {
|
||||||
@apply p-4 rounded-t-lg
|
@apply p-4 rounded-t-lg
|
||||||
}
|
}
|
||||||
47
web/src/css/theme.css
Normal file
47
web/src/css/theme.css
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
.light {
|
||||||
|
--primary: 36, 39, 52;
|
||||||
|
--primary-hover: 17, 24, 39;
|
||||||
|
--secondary-hover: 243, 244, 246;
|
||||||
|
|
||||||
|
--background: 255, 255, 255;
|
||||||
|
--content: 0, 0, 0;
|
||||||
|
|
||||||
|
--input-background: 249, 250, 251;
|
||||||
|
--input-border: 209, 213, 219;
|
||||||
|
--input-border-focus: 36, 39, 52;
|
||||||
|
|
||||||
|
--menu-background: 255, 255, 255;
|
||||||
|
--menu-item-background-hover: 243, 244, 246;
|
||||||
|
--menu-item-background-focus: 229, 231, 235;
|
||||||
|
|
||||||
|
--footer-background: 36, 39, 52;
|
||||||
|
|
||||||
|
--separator: 17, 24, 39;
|
||||||
|
|
||||||
|
background: rgba(var(--background));
|
||||||
|
color: rgba(var(--content));
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark {
|
||||||
|
--primary: 36, 39, 52;
|
||||||
|
--primary-hover: 44, 48, 64;
|
||||||
|
--secondary-hover: 36, 39, 52;
|
||||||
|
|
||||||
|
--background: 7, 10, 36;
|
||||||
|
--content: 255, 255, 255;
|
||||||
|
|
||||||
|
--input-background: 36, 39, 52;
|
||||||
|
--input-border: 43, 46, 61;
|
||||||
|
--input-border-focus: 108, 115, 150;
|
||||||
|
|
||||||
|
--menu-background: 33, 36, 54;
|
||||||
|
--menu-item-background-hover: 43, 46, 69;
|
||||||
|
--menu-item-background-focus: 25, 26, 41;
|
||||||
|
|
||||||
|
--footer-background: 10, 14, 48;
|
||||||
|
|
||||||
|
--separator: 108, 115, 150;
|
||||||
|
|
||||||
|
background: rgba(var(--background));
|
||||||
|
color: rgba(var(--content));
|
||||||
|
}
|
||||||
@@ -36,19 +36,17 @@
|
|||||||
|
|
||||||
<div class="dropdown relative">
|
<div class="dropdown relative">
|
||||||
<button
|
<button
|
||||||
class="flex items-center justify-center"
|
class="btn-icon flex items-center justify-center"
|
||||||
on:click={toggleMenu}
|
on:click={toggleMenu}
|
||||||
type="button"
|
type="button"
|
||||||
>
|
>
|
||||||
<slot>
|
<slot>
|
||||||
<i
|
<i class="fa fa-ellipsis-vertical text-{size}"></i>
|
||||||
class="fa fa-ellipsis-vertical text-{size} hover:bg-gray-300 hover:bg-opacity-50 px-[14px] py-2 rounded-full"
|
|
||||||
></i>
|
|
||||||
</slot>
|
</slot>
|
||||||
</button>
|
</button>
|
||||||
{#if isOpen}
|
{#if isOpen}
|
||||||
<ul
|
<ul
|
||||||
class="menu absolute bg-white border rounded-l-xl rounded-b-xl shadow-md right-0 overflow-hidden text-black mt-1"
|
class="menu absolute bg-menu-background border border-input-border rounded-l-xl rounded-b-xl shadow-md right-0 overflow-hidden mt-2"
|
||||||
class:none={isOpen}
|
class:none={isOpen}
|
||||||
style="z-index: 1001"
|
style="z-index: 1001"
|
||||||
>
|
>
|
||||||
@@ -56,7 +54,7 @@
|
|||||||
<!-- svelte-ignore a11y-no-noninteractive-element-interactions -->
|
<!-- svelte-ignore a11y-no-noninteractive-element-interactions -->
|
||||||
<!-- svelte-ignore a11y-no-noninteractive-tabindex -->
|
<!-- svelte-ignore a11y-no-noninteractive-tabindex -->
|
||||||
<li
|
<li
|
||||||
class="menu-item flex items-center px-4 py-3 cursor-pointer hover:bg-gray-100 focus:bg-gray-200 transition-colors"
|
class="menu-item flex items-center px-4 py-3 cursor-pointer hover:bg-menu-item-background-hover focus:bg-menu-item-background-focus transition-colors"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
on:mouseup|stopPropagation={() => handleItemClick(item)}
|
on:mouseup|stopPropagation={() => handleItemClick(item)}
|
||||||
on:keydown|stopPropagation={() => handleItemClick(item)}
|
on:keydown|stopPropagation={() => handleItemClick(item)}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@
|
|||||||
</h3>
|
</h3>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="rounded-full py-1 px-[10px] hover:bg-gray-100 focus:ring-4 ring-gray-200 transition-colors"
|
class="rounded-full btn-icon"
|
||||||
on:click={closeModal}
|
on:click={closeModal}
|
||||||
>
|
>
|
||||||
<i class="fa fa-close"></i>
|
<i class="fa fa-close"></i>
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
|
|
||||||
function handleRadioChange(radioIndex: number) {
|
function handleRadioChange(radioIndex: number) {
|
||||||
selected = radioIndex;
|
selected = radioIndex;
|
||||||
dispatch('change', items[selected])
|
dispatch("change", items[selected]);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -29,13 +29,9 @@
|
|||||||
type="radio"
|
type="radio"
|
||||||
checked={i == selected}
|
checked={i == selected}
|
||||||
value={item.value}
|
value={item.value}
|
||||||
class="w-4 h-4 text-primary accent-primary bg-gray-100 border-gray-300 focus:ring-gray-400 focus:ring-2"
|
class="w-4 h-4 accent-input-background border-input-border focus:ring-input-border-focus focus:ring-2"
|
||||||
on:change={() => handleRadioChange(i)}
|
on:change={() => handleRadioChange(i)}
|
||||||
/>
|
/>
|
||||||
<label
|
<label for="{name}-radio-{i}" class="ms-2 text-sm">{item.text}</label>
|
||||||
for="{name}-radio-{i}"
|
|
||||||
class="ms-2 text-sm text-gray-900 dark:text-gray-300"
|
|
||||||
>{item.text}</label
|
|
||||||
>
|
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
|
|||||||
@@ -54,13 +54,13 @@
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="relative text-gray-600 {extraClasses}">
|
<div class="relative {extraClasses}">
|
||||||
<span class="absolute top-1/2 -translate-y-1/2 left-0 pl-4">
|
<span class="absolute top-1/2 -translate-y-1/2 left-0 pl-4">
|
||||||
<i class="fa fa-search" class:text-xl={large}></i>
|
<i class="fa fa-search" class:text-xl={large}></i>
|
||||||
</span>
|
</span>
|
||||||
{#if value.length > 0}
|
{#if value.length > 0}
|
||||||
<button
|
<button
|
||||||
class="absolute top-1/2 -translate-y-1/2 right-0 h-6 w-6 mr-2 hover:bg-gray-300 hover:bg-opacity-50 rounded-full"
|
class="btn-icon absolute top-1/2 -translate-y-1/2 right-0 mr-2"
|
||||||
on:click={clear}
|
on:click={clear}
|
||||||
in:fade={{ duration: 150 }}
|
in:fade={{ duration: 150 }}
|
||||||
out:fade={{ duration: 150 }}
|
out:fade={{ duration: 150 }}
|
||||||
@@ -84,7 +84,7 @@
|
|||||||
|
|
||||||
{#if dropDownOpen}
|
{#if dropDownOpen}
|
||||||
<ul
|
<ul
|
||||||
class="menu absolute bg-white border rounded-xl shadow-md overflow-hidden text-black w-full"
|
class="menu absolute bg-menu-background border border-input-border rounded-xl shadow-md overflow-hidden w-full"
|
||||||
class:none={!dropDownOpen}
|
class:none={!dropDownOpen}
|
||||||
style="z-index: 1001"
|
style="z-index: 1001"
|
||||||
>
|
>
|
||||||
@@ -92,7 +92,7 @@
|
|||||||
<!-- svelte-ignore a11y-no-noninteractive-element-interactions -->
|
<!-- svelte-ignore a11y-no-noninteractive-element-interactions -->
|
||||||
<!-- svelte-ignore a11y-no-noninteractive-tabindex -->
|
<!-- svelte-ignore a11y-no-noninteractive-tabindex -->
|
||||||
<li
|
<li
|
||||||
class="menu-item flex items-center px-4 py-3 cursor-pointer hover:bg-gray-100 focus:bg-gray-200 transition-colors"
|
class="menu-item flex items-center px-4 py-3 cursor-pointer hover:bg-menu-item-background-hover focus:bg-menu-item-background-focus transition-colors"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
on:mousedown|stopPropagation={() => handleItemClick(item)}
|
on:mousedown|stopPropagation={() => handleItemClick(item)}
|
||||||
on:keydown|stopPropagation={() => handleItemClick(item)}
|
on:keydown|stopPropagation={() => handleItemClick(item)}
|
||||||
|
|||||||
@@ -29,7 +29,7 @@
|
|||||||
{/if}
|
{/if}
|
||||||
<select
|
<select
|
||||||
{name}
|
{name}
|
||||||
class="bg-gray-50 h-10 w-full px-4 border-r-8 border-transparent outline outline-1 outline-gray-200 rounded-md focus:outline-primary transition-colors"
|
class="bg-input-background h-10 w-full px-4 border-r-8 border-transparent outline outline-1 outline-input-border rounded-md focus:outline-input-border-focus transition-colors"
|
||||||
bind:value
|
bind:value
|
||||||
on:change={(e) => onChange(e.target)}
|
on:change={(e) => onChange(e.target)}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -37,6 +37,9 @@
|
|||||||
<div class="my-4" id="slider" bind:this={sliderContainer}></div>
|
<div class="my-4" id="slider" bind:this={sliderContainer}></div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
:global(.noUi-target) {
|
||||||
|
@apply border border-input-border shadow-none;
|
||||||
|
}
|
||||||
:global(.noUi-horizontal) {
|
:global(.noUi-horizontal) {
|
||||||
height: 6px;
|
height: 6px;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
export let icon: string = "";
|
export let icon: string = "";
|
||||||
export let extraClasses: string = "";
|
export let extraClasses: string = "";
|
||||||
export let type: "text" | "password" | "search" = "text";
|
export let type: "text" | "password" | "search" = "text";
|
||||||
export let autocomplete: "on" | "off" = "on"
|
export let autocomplete: "on" | "off" = "on";
|
||||||
function typeAction(node: HTMLInputElement) {
|
function typeAction(node: HTMLInputElement) {
|
||||||
node.type = type;
|
node.type = type;
|
||||||
}
|
}
|
||||||
@@ -25,7 +25,7 @@
|
|||||||
{/if}
|
{/if}
|
||||||
<input
|
<input
|
||||||
{name}
|
{name}
|
||||||
class="bg-gray-50 border rounded-md p-3 transition-colors focus:border-primary focus:outline-none focus:ring-0 w-full {extraClasses}"
|
class="bg-input-background border border-input-border rounded-md p-3 transition-colors focus:border-input-border-focus focus:outline-none focus:ring-0 w-full {extraClasses}"
|
||||||
class:border-red-400={error.length > 0}
|
class:border-red-400={error.length > 0}
|
||||||
class:bg-red-50={error.length > 0}
|
class:bg-red-50={error.length > 0}
|
||||||
{autocomplete}
|
{autocomplete}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
{/if}
|
{/if}
|
||||||
<textarea
|
<textarea
|
||||||
{name}
|
{name}
|
||||||
class="bg-gray-50 border rounded-md p-3 resize-none transition-colors focus:border-primary focus:outline-none focus:ring-0 w-full"
|
class="bg-input-background border border-input-border rounded-md p-3 resize-none transition-colors focus:border-input-border-focus focus:outline-none focus:ring-0 w-full"
|
||||||
{rows}
|
{rows}
|
||||||
{placeholder}
|
{placeholder}
|
||||||
class:border-red-400={error.length > 0}
|
class:border-red-400={error.length > 0}
|
||||||
|
|||||||
@@ -6,9 +6,15 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<label class="relative my-2 inline-flex items-center cursor-pointer">
|
<label class="relative my-2 inline-flex items-center cursor-pointer">
|
||||||
<input {name} bind:checked={value} type="checkbox" class="sr-only peer" value="1"/>
|
<input
|
||||||
|
{name}
|
||||||
|
bind:checked={value}
|
||||||
|
type="checkbox"
|
||||||
|
class="sr-only peer"
|
||||||
|
value="1"
|
||||||
|
/>
|
||||||
<div
|
<div
|
||||||
class="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-gray-400 rounded-full peer peer-checked:after:translate-x-full rtl:peer-checked:after:-translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:start-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all dark:border-gray-600 peer-checked:bg-primary"
|
class="w-11 h-6 bg-input-background peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-input-border-focus rounded-full peer peer-checked:after:translate-x-full rtl:peer-checked:after:-translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:start-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-input-background"
|
||||||
></div>
|
></div>
|
||||||
<span class="ms-3 text-sm font-medium">{label}</span>
|
<span class="ms-3 text-sm font-medium">{label}</span>
|
||||||
</label>
|
</label>
|
||||||
|
|||||||
@@ -4,17 +4,35 @@ Command: npx @threlte/gltf@2.0.1 static/models/earth.glb
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { DirectionalLight, Group, PointLight } from "three";
|
import { theme } from "$lib/stores/theme_store";
|
||||||
import { T, forwardEventHandlers } from "@threlte/core";
|
import { T, forwardEventHandlers } from "@threlte/core";
|
||||||
import { useGltf } from "@threlte/extras";
|
import { useGltf } from "@threlte/extras";
|
||||||
|
import { backIn, backInOut, backOut, cubicInOut } from "svelte/easing";
|
||||||
|
import { tweened } from "svelte/motion";
|
||||||
|
import { Group } from "three";
|
||||||
|
|
||||||
export const ref = new Group();
|
export const ref = new Group();
|
||||||
|
|
||||||
export let rotation: number = 0;
|
export let rotation: number = 0;
|
||||||
|
|
||||||
const gltf = useGltf("/models/earth.glb");
|
const gltf = useGltf("/models/earth2.glb");
|
||||||
|
|
||||||
const component = forwardEventHandlers();
|
const component = forwardEventHandlers();
|
||||||
|
|
||||||
|
const sunXRotation = tweened(0, {
|
||||||
|
duration: 1500,
|
||||||
|
easing: backInOut
|
||||||
|
});
|
||||||
|
|
||||||
|
const sunLightIntensitiy = tweened(2.5, {
|
||||||
|
duration: 500,
|
||||||
|
});
|
||||||
|
const moonLightIntensitiy = tweened(2.5, {
|
||||||
|
duration: 500,
|
||||||
|
});
|
||||||
|
$: sunLightIntensitiy.set($theme == "light" ? 2.5 : 0.1);
|
||||||
|
$: moonLightIntensitiy.set($theme == "dark" ? 2.5 : 0.1);
|
||||||
|
$: sunXRotation.set($theme == "light" ? 0 : -Math.PI);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<T is={ref} dispose={false} {...$$restProps} bind:this={$component}>
|
<T is={ref} dispose={false} {...$$restProps} bind:this={$component}>
|
||||||
@@ -88,18 +106,47 @@ Command: npx @threlte/gltf@2.0.1 static/models/earth.glb
|
|||||||
|
|
||||||
<!-- /Airplane -->
|
<!-- /Airplane -->
|
||||||
<!-- Sun -->
|
<!-- Sun -->
|
||||||
|
<T.Group rotation={[$sunXRotation, 0, 0]}>
|
||||||
<T.Mesh
|
<T.Mesh
|
||||||
geometry={gltf.nodes.Icosphere007.geometry}
|
geometry={gltf.nodes.Icosphere007.geometry}
|
||||||
material={gltf.materials["Material.009"]}
|
material={gltf.materials["Material.009"]}
|
||||||
position={[0.5, 2, -0.5]}
|
position={[0, 2, 0]}
|
||||||
scale={0.88}
|
scale={0.88}
|
||||||
>
|
>
|
||||||
<T.PointLight position={[2, 0, 0]} color="#ffffff" intensity={4.5}
|
<T.PointLight position={[0, 0.4, 0]} color="#fff4ab" intensity={0.15}
|
||||||
></T.PointLight>
|
></T.PointLight>
|
||||||
|
<T.PointLight position={[0, 0, 0.4]} color="#fff4ab" intensity={0.15}
|
||||||
<T.DirectionalLight color="#f4de51" intensity={2.5} castShadow
|
></T.PointLight>
|
||||||
|
<T.PointLight position={[0, -0.4, 0]} color="#fff4ab" intensity={0.15}
|
||||||
|
></T.PointLight>
|
||||||
|
<T.DirectionalLight
|
||||||
|
position={[0, -0.5, 0]}
|
||||||
|
color="#ffffff"
|
||||||
|
intensity={$sunLightIntensitiy}
|
||||||
|
castShadow
|
||||||
></T.DirectionalLight>
|
></T.DirectionalLight>
|
||||||
</T.Mesh>
|
</T.Mesh>
|
||||||
|
<T.Mesh
|
||||||
|
geometry={gltf.nodes.Moon01.geometry}
|
||||||
|
material={gltf.materials["Material.011"]}
|
||||||
|
position={[0, -2, 0]}
|
||||||
|
rotation={[Math.PI / 2, 0, Math.PI]}
|
||||||
|
scale={0.055}
|
||||||
|
>
|
||||||
|
<T.PointLight position={[0, 0.4, 0]} color="#a2cbf5" intensity={0.15}
|
||||||
|
></T.PointLight>
|
||||||
|
<T.PointLight position={[-3, 0, 0.4]} color="#a2cbf5" intensity={0.05}
|
||||||
|
></T.PointLight>
|
||||||
|
<T.PointLight position={[0, -0.4, 0]} color="#a2cbf5" intensity={0.15}
|
||||||
|
></T.PointLight>
|
||||||
|
<T.DirectionalLight
|
||||||
|
position={[0, -0.5, 0]}
|
||||||
|
color="#a2cbf5"
|
||||||
|
intensity={$moonLightIntensitiy}
|
||||||
|
castShadow
|
||||||
|
></T.DirectionalLight>
|
||||||
|
</T.Mesh>
|
||||||
|
</T.Group>
|
||||||
<!-- /Sun -->
|
<!-- /Sun -->
|
||||||
<T.Group scale={1} rotation.y={rotation}>
|
<T.Group scale={1} rotation.y={rotation}>
|
||||||
<!-- Earth -->
|
<!-- Earth -->
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
import LogoTextLight from "./logo/logo_text_light.svelte";
|
import LogoTextLight from "./logo/logo_text_light.svelte";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<footer class="bg-primary text-white w-full grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-y-8 p-12 mt-12 rounded-t-3xl">
|
<footer class="bg-footer-background text-white w-full grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-y-8 p-12 mt-12 rounded-t-3xl">
|
||||||
<LogoTextLight></LogoTextLight>
|
<LogoTextLight></LogoTextLight>
|
||||||
<div>
|
<div>
|
||||||
<h5 class="font-semibold">Resources</h5>
|
<h5 class="font-semibold">Resources</h5>
|
||||||
|
|||||||
@@ -5,6 +5,8 @@
|
|||||||
import { tweened } from "svelte/motion";
|
import { tweened } from "svelte/motion";
|
||||||
import Dropdown from "./base/dropdown.svelte";
|
import Dropdown from "./base/dropdown.svelte";
|
||||||
import { cubicOut } from "svelte/easing";
|
import { cubicOut } from "svelte/easing";
|
||||||
|
import { theme, toggleTheme } from "$lib/stores/theme_store";
|
||||||
|
import LogoTextLight from "./logo/logo_text_light.svelte";
|
||||||
|
|
||||||
const navBarItems = [
|
const navBarItems = [
|
||||||
{ text: "Home", value: "/" },
|
{ text: "Home", value: "/" },
|
||||||
@@ -49,16 +51,22 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<nav class="flex justify-between items-center p-6">
|
<nav class="flex justify-between items-center p-6">
|
||||||
<a href="/" data-sveltekit-preload-data="tap"><LogoText></LogoText></a>
|
<a href="/" data-sveltekit-preload-data="tap">
|
||||||
|
{#if $theme == "light"}
|
||||||
|
<LogoText></LogoText>
|
||||||
|
{:else}
|
||||||
|
<LogoTextLight></LogoTextLight>
|
||||||
|
{/if}
|
||||||
|
</a>
|
||||||
{#if $currentUser}
|
{#if $currentUser}
|
||||||
<menu class="flex gap-8 relative py-1 px-2">
|
<menu class="flex gap-8 relative py-1 px-2">
|
||||||
<div
|
<div
|
||||||
class="absolute h-full w-16 bg-slate-400 rounded-xl opacity-20 top-0"
|
class="absolute h-full w-16 bg-menu-background rounded-xl top-0 z-0"
|
||||||
style="width: {$indicatorWidth}px; left: {$indicatorPosition}px"
|
style="width: {$indicatorWidth}px; left: {$indicatorPosition}px"
|
||||||
></div>
|
></div>
|
||||||
{#each navBarItems as item}
|
{#each navBarItems as item}
|
||||||
<a
|
<a
|
||||||
class="font-semibold"
|
class="font-semibold z-10"
|
||||||
on:click={(e) => setActiveItem(e.target)}
|
on:click={(e) => setActiveItem(e.target)}
|
||||||
href={item.value}
|
href={item.value}
|
||||||
data-sveltekit-preload-data="tap">{item.text}</a
|
data-sveltekit-preload-data="tap">{item.text}</a
|
||||||
@@ -66,6 +74,12 @@
|
|||||||
{/each}
|
{/each}
|
||||||
</menu>
|
</menu>
|
||||||
<div class="flex gap-6 items-center">
|
<div class="flex gap-6 items-center">
|
||||||
|
<button
|
||||||
|
class="btn-icon fa-regular fa-{$theme === 'light'
|
||||||
|
? 'sun'
|
||||||
|
: 'moon'}"
|
||||||
|
on:click={() => toggleTheme()}
|
||||||
|
></button>
|
||||||
<a class="btn-primary btn-large" href="/trail/edit/new"
|
<a class="btn-primary btn-large" href="/trail/edit/new"
|
||||||
><i class="fa fa-plus mr-2"></i>New Trail</a
|
><i class="fa fa-plus mr-2"></i>New Trail</a
|
||||||
>
|
>
|
||||||
@@ -74,7 +88,7 @@
|
|||||||
on:change={(e) => handleDropdownClick(e.detail)}
|
on:change={(e) => handleDropdownClick(e.detail)}
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
class="w-12 h-h12 rounded-full"
|
class="rounded-full"
|
||||||
src="https://api.dicebear.com/7.x/initials/svg?seed={$currentUser.username}&backgroundType=gradientLinear"
|
src="https://api.dicebear.com/7.x/initials/svg?seed={$currentUser.username}&backgroundType=gradientLinear"
|
||||||
alt=""
|
alt=""
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,11 +1,20 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import { theme } from "$lib/stores/theme_store";
|
||||||
import { T, useTask } from "@threlte/core";
|
import { T, useTask } from "@threlte/core";
|
||||||
|
import { tweened } from "svelte/motion";
|
||||||
import Earth from "./earth.svelte";
|
import Earth from "./earth.svelte";
|
||||||
|
|
||||||
let rotation = 0;
|
let rotation = 0;
|
||||||
useTask((delta) => {
|
useTask((delta) => {
|
||||||
rotation += delta/4;
|
rotation += delta / 4;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const ambientIntensitiy = tweened(1.2, {
|
||||||
|
duration: 500,
|
||||||
|
});
|
||||||
|
|
||||||
|
$: ambientColor = $theme == "light" ? "#ffffff" : "#4c7fe6";
|
||||||
|
$: ambientIntensitiy.set($theme == "light" ? 1.2 : 0);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<T.PerspectiveCamera
|
<T.PerspectiveCamera
|
||||||
@@ -16,6 +25,6 @@
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<T.AmbientLight color="#ffffff" intensity={1.1} />
|
<T.AmbientLight color={ambientColor} intensity={$ambientIntensitiy} />
|
||||||
|
|
||||||
<Earth rotation={rotation}></Earth>
|
<Earth {rotation}></Earth>
|
||||||
|
|||||||
@@ -35,7 +35,7 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="trail-card rounded-2xl border sm:w-72 cursor-pointer"
|
class="trail-card rounded-2xl border border-input-border sm:w-72 cursor-pointer"
|
||||||
on:mouseenter
|
on:mouseenter
|
||||||
on:mouseleave
|
on:mouseleave
|
||||||
role="listitem"
|
role="listitem"
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { goto } from "$app/navigation";
|
import { goto } from "$app/navigation";
|
||||||
import type { Trail } from "$lib/models/trail";
|
import type { Trail } from "$lib/models/trail";
|
||||||
import { trails_delete, trails_index } from "$lib/stores/trail_store";
|
|
||||||
import { currentUser } from "$lib/stores/user_store";
|
import { currentUser } from "$lib/stores/user_store";
|
||||||
import { formatMeters, formatTimeHHMM } from "$lib/util/format_util";
|
import { formatMeters, formatTimeHHMM } from "$lib/util/format_util";
|
||||||
import Dropdown, { type DropdownItem } from "../base/dropdown.svelte";
|
import Dropdown, { type DropdownItem } from "../base/dropdown.svelte";
|
||||||
@@ -29,7 +28,7 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<li
|
<li
|
||||||
class="flex gap-8 p-4 rounded-xl border cursor-pointer hover:bg-gray-100 transition-colors"
|
class="flex gap-8 p-4 rounded-xl border border-input-border cursor-pointer hover:bg-secondary-hover transition-colors"
|
||||||
>
|
>
|
||||||
<div class="shrink-0">
|
<div class="shrink-0">
|
||||||
<img
|
<img
|
||||||
|
|||||||
@@ -6,8 +6,6 @@ import { pb } from './pocketbase'
|
|||||||
|
|
||||||
export function createInstance() {
|
export function createInstance() {
|
||||||
if(pb.authStore.model) {
|
if(pb.authStore.model) {
|
||||||
console.log(pb.authStore.model.token);
|
|
||||||
|
|
||||||
return new MeiliSearch({ host: PUBLIC_MEILISEARCH_URL, apiKey: pb.authStore.model.token })
|
return new MeiliSearch({ host: PUBLIC_MEILISEARCH_URL, apiKey: pb.authStore.model.token })
|
||||||
}
|
}
|
||||||
return new MeiliSearch({ host: PUBLIC_MEILISEARCH_URL, apiKey: PUBLIC_MEILISEARCH_API_TOKEN })
|
return new MeiliSearch({ host: PUBLIC_MEILISEARCH_URL, apiKey: PUBLIC_MEILISEARCH_API_TOKEN })
|
||||||
|
|||||||
14
web/src/lib/stores/theme_store.ts
Normal file
14
web/src/lib/stores/theme_store.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import { get, writable, type Writable } from "svelte/store";
|
||||||
|
|
||||||
|
type Theme = "dark" | "light"
|
||||||
|
|
||||||
|
export const theme: Writable<Theme> = writable("light");
|
||||||
|
|
||||||
|
|
||||||
|
export function toggleTheme() {
|
||||||
|
if (get(theme) === "light") {
|
||||||
|
theme.set("dark")
|
||||||
|
} else {
|
||||||
|
theme.set("light");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,12 +2,18 @@
|
|||||||
import Toast from "$lib/components/base/toast.svelte";
|
import Toast from "$lib/components/base/toast.svelte";
|
||||||
import Footer from "$lib/components/footer.svelte";
|
import Footer from "$lib/components/footer.svelte";
|
||||||
import NavBar from "$lib/components/nav_bar.svelte";
|
import NavBar from "$lib/components/nav_bar.svelte";
|
||||||
import "../app.css";
|
import { theme } from "$lib/stores/theme_store";
|
||||||
import "../components.css"
|
import "@fortawesome/fontawesome-free/css/all.min.css";
|
||||||
import '@fortawesome/fontawesome-free/css/all.min.css'
|
import "../css/app.css";
|
||||||
|
import "../css/components.css";
|
||||||
|
import "../css/theme.css";
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<NavBar></NavBar>
|
<div class="theme {$theme}">
|
||||||
<Toast></Toast>
|
<NavBar></NavBar>
|
||||||
<slot />
|
<Toast></Toast>
|
||||||
<Footer></Footer>
|
<slot />
|
||||||
|
|
||||||
|
<Footer></Footer>
|
||||||
|
</div>
|
||||||
|
|||||||
@@ -83,7 +83,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<section class="max-w-7xl mx-auto mt-8 px-8 xl:px-0">
|
<section class="max-w-7xl mx-auto mt-8 px-8 xl:px-0">
|
||||||
<h2 class="text-4xl md:text-5xl font-bold text-primary">
|
<h2 class="text-4xl md:text-5xl font-bold">
|
||||||
{$currentUser ? "Your" : "Explore"} trails
|
{$currentUser ? "Your" : "Explore"} trails
|
||||||
</h2>
|
</h2>
|
||||||
<div
|
<div
|
||||||
@@ -98,7 +98,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<section class="max-w-7xl mx-auto mt-8 px-8 xl:px-0">
|
<section class="max-w-7xl mx-auto mt-8 px-8 xl:px-0">
|
||||||
<h2 class="text-4xl md:text-5xl font-bold text-primary">By category</h2>
|
<h2 class="text-4xl md:text-5xl font-bold">By category</h2>
|
||||||
<div
|
<div
|
||||||
id="categories"
|
id="categories"
|
||||||
class="grid grid-cols- sm:grid-cols-2 lg:grid-cols-4 justify-items-center gap-8 py-8"
|
class="grid grid-cols- sm:grid-cols-2 lg:grid-cols-4 justify-items-center gap-8 py-8"
|
||||||
|
|||||||
@@ -358,7 +358,7 @@
|
|||||||
style="display: none;"
|
style="display: none;"
|
||||||
on:change={handleFileSelection}
|
on:change={handleFileSelection}
|
||||||
/>
|
/>
|
||||||
<hr />
|
<hr class="border-separator" />
|
||||||
<h3 class="text-xl font-semibold">Basic Info</h3>
|
<h3 class="text-xl font-semibold">Basic Info</h3>
|
||||||
<div class="flex gap-4 justify-around">
|
<div class="flex gap-4 justify-around">
|
||||||
<div class="flex flex-col items-center">
|
<div class="flex flex-col items-center">
|
||||||
@@ -413,7 +413,7 @@
|
|||||||
></Select>
|
></Select>
|
||||||
{/if}
|
{/if}
|
||||||
<Toggle name="public" label="Public" bind:value={$form.public}></Toggle>
|
<Toggle name="public" label="Public" bind:value={$form.public}></Toggle>
|
||||||
<hr />
|
<hr class="border-separator"/>
|
||||||
<h3 class="text-xl font-semibold">Waypoints</h3>
|
<h3 class="text-xl font-semibold">Waypoints</h3>
|
||||||
<ul>
|
<ul>
|
||||||
{#each $form.expand.waypoints ?? [] as waypoint, i}
|
{#each $form.expand.waypoints ?? [] as waypoint, i}
|
||||||
@@ -433,7 +433,7 @@
|
|||||||
on:click={beforeWaypointModalOpen}
|
on:click={beforeWaypointModalOpen}
|
||||||
><i class="fa fa-plus mr-2"></i>Add Waypoint</button
|
><i class="fa fa-plus mr-2"></i>Add Waypoint</button
|
||||||
>
|
>
|
||||||
<hr />
|
<hr class="border-separator"/>
|
||||||
<h3 class="text-xl font-semibold">Photos</h3>
|
<h3 class="text-xl font-semibold">Photos</h3>
|
||||||
<div class="flex gap-4 max-w-full overflow-x-scroll shrink-0">
|
<div class="flex gap-4 max-w-full overflow-x-scroll shrink-0">
|
||||||
<button
|
<button
|
||||||
@@ -460,7 +460,7 @@
|
|||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
<hr />
|
<hr class="border-separator"/>
|
||||||
<h3 class="text-xl font-semibold">Summit Book</h3>
|
<h3 class="text-xl font-semibold">Summit Book</h3>
|
||||||
<ul>
|
<ul>
|
||||||
{#each $form.expand.summit_logs ?? [] as log, i}
|
{#each $form.expand.summit_logs ?? [] as log, i}
|
||||||
@@ -479,7 +479,7 @@
|
|||||||
on:click={beforeSummitLogModalOpen}
|
on:click={beforeSummitLogModalOpen}
|
||||||
><i class="fa fa-plus mr-2"></i>Add Entry</button
|
><i class="fa fa-plus mr-2"></i>Add Entry</button
|
||||||
>
|
>
|
||||||
<hr />
|
<hr class="border-separator"/>
|
||||||
<Button
|
<Button
|
||||||
primary={true}
|
primary={true}
|
||||||
large={true}
|
large={true}
|
||||||
|
|||||||
@@ -158,10 +158,11 @@
|
|||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
{#if $currentUser && $currentUser.id == $trail.author}
|
{#if $currentUser && $currentUser.id == $trail.author}
|
||||||
<div class="px-4 py-2 bg-white rounded-full space-x-2">
|
<div class="px-4 py-2 bg-menu-background rounded-full space-x-2">
|
||||||
{#each dropdownItems as item}
|
{#each dropdownItems as item}
|
||||||
<button
|
<button
|
||||||
class="rounded-full py-1 px-[10px] hover:bg-gray-100 focus:ring-4 ring-gray-200 transition-colors"
|
data-title={item.text}
|
||||||
|
class="tooltip btn-icon"
|
||||||
on:click={() => handleDropdownClick(item)}
|
on:click={() => handleDropdownClick(item)}
|
||||||
><i class="fa fa-{item.icon}"></i></button
|
><i class="fa fa-{item.icon}"></i></button
|
||||||
>
|
>
|
||||||
@@ -201,13 +202,13 @@
|
|||||||
{/if}
|
{/if}
|
||||||
</section>
|
</section>
|
||||||
{#if $trail.tags && $trail.tags.length > 0}
|
{#if $trail.tags && $trail.tags.length > 0}
|
||||||
<hr />
|
<hr class="border-separator"/>
|
||||||
<section class="flex p-8 gap-4 text-gray-600">
|
<section class="flex p-8 gap-4 text-gray-600">
|
||||||
{#each $trail.tags as tag}
|
{#each $trail.tags as tag}
|
||||||
<span class="py-2 px-4 border rounded-full">{tag}</span>
|
<span class="py-2 px-4 border rounded-full">{tag}</span>
|
||||||
{/each}
|
{/each}
|
||||||
</section>
|
</section>
|
||||||
<hr />
|
<hr class="border-separator"/>
|
||||||
{/if}
|
{/if}
|
||||||
<section class="p-8">
|
<section class="p-8">
|
||||||
<Tabs {tabs} bind:activeTab></Tabs>
|
<Tabs {tabs} bind:activeTab></Tabs>
|
||||||
|
|||||||
@@ -169,7 +169,7 @@
|
|||||||
<main
|
<main
|
||||||
class="grid grid-cols-1 md:grid-cols-[300px_1fr] gap-8 max-w-7xl mx-6 md:mx-auto"
|
class="grid grid-cols-1 md:grid-cols-[300px_1fr] gap-8 max-w-7xl mx-6 md:mx-auto"
|
||||||
>
|
>
|
||||||
<div class="trail-filters p-8 border rounded-xl">
|
<div class="trail-filters p-8 border border-input-border rounded-xl">
|
||||||
<div class="flex gap-2 items-center">
|
<div class="flex gap-2 items-center">
|
||||||
<div class="basis-full">
|
<div class="basis-full">
|
||||||
<Search
|
<Search
|
||||||
@@ -180,14 +180,14 @@
|
|||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
id="sort-order-btn"
|
id="sort-order-btn"
|
||||||
class="rounded-full py-1 px-2 hover:bg-gray-100 focus:ring-4 ring-gray-200 transition-colors md:hidden"
|
class="btn-icon md:hidden"
|
||||||
on:click={() => (filterExpanded = !filterExpanded)}
|
on:click={() => (filterExpanded = !filterExpanded)}
|
||||||
><i class="fa fa-sliders"></i></button
|
><i class="fa fa-sliders"></i></button
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#if filterExpanded}
|
{#if filterExpanded}
|
||||||
<hr class="my-4" />
|
<hr class="my-4 border-separator" />
|
||||||
<p class="text-sm font-medium pb-4">Category</p>
|
<p class="text-sm font-medium pb-4">Category</p>
|
||||||
{#each $categories as category, i}
|
{#each $categories as category, i}
|
||||||
<div class="flex items-center mb-4">
|
<div class="flex items-center mb-4">
|
||||||
@@ -195,17 +195,17 @@
|
|||||||
id="{category.name}-checkbox"
|
id="{category.name}-checkbox"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
value={category.id}
|
value={category.id}
|
||||||
class="w-4 h-4 text-primary accent-primary bg-gray-100 border-gray-300 focus:ring-gray-400 focus:ring-2"
|
class="w-4 h-4 accent-input-background border-input-border focus:ring-input-border-focus focus:ring-2"
|
||||||
on:change={() => setCategoryFilter(category)}
|
on:change={() => setCategoryFilter(category)}
|
||||||
/>
|
/>
|
||||||
<label
|
<label
|
||||||
for="{category.name}-checkbox"
|
for="{category.name}-checkbox"
|
||||||
class="ms-2 text-sm text-gray-900 dark:text-gray-300"
|
class="ms-2 text-sm"
|
||||||
>{category.name}</label
|
>{category.name}</label
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
<hr class="my-4" />
|
<hr class="my-4 border-separator" />
|
||||||
<p class="text-sm font-medium pb-4">Near</p>
|
<p class="text-sm font-medium pb-4">Near</p>
|
||||||
<div class="mb-8">
|
<div class="mb-8">
|
||||||
<Search
|
<Search
|
||||||
@@ -225,7 +225,7 @@
|
|||||||
<span class="text-gray-500 text-sm">Radius:</span>
|
<span class="text-gray-500 text-sm">Radius:</span>
|
||||||
{formatMeters(filter.near.radius)}
|
{formatMeters(filter.near.radius)}
|
||||||
</p>
|
</p>
|
||||||
<hr class="my-4" />
|
<hr class="my-4 border-separator" />
|
||||||
<p class="text-sm font-medium pb-4">Distance</p>
|
<p class="text-sm font-medium pb-4">Distance</p>
|
||||||
<DoubleSlider
|
<DoubleSlider
|
||||||
minValue={minDistance}
|
minValue={minDistance}
|
||||||
@@ -238,7 +238,7 @@
|
|||||||
<span>{formatMeters(filter.distanceMin)}</span>
|
<span>{formatMeters(filter.distanceMin)}</span>
|
||||||
<span>{formatMeters(filter.distanceMax)}</span>
|
<span>{formatMeters(filter.distanceMax)}</span>
|
||||||
</div>
|
</div>
|
||||||
<hr class="my-4" />
|
<hr class="my-4 border-separator" />
|
||||||
<p class="text-sm font-medium pb-4">Elevation Gain</p>
|
<p class="text-sm font-medium pb-4">Elevation Gain</p>
|
||||||
<DoubleSlider
|
<DoubleSlider
|
||||||
minValue={minElevationGain}
|
minValue={minElevationGain}
|
||||||
@@ -251,7 +251,7 @@
|
|||||||
<span>{formatMeters(filter.eleavationGainMin)}</span>
|
<span>{formatMeters(filter.eleavationGainMin)}</span>
|
||||||
<span>{formatMeters(filter.elevationGainMax)}</span>
|
<span>{formatMeters(filter.elevationGainMax)}</span>
|
||||||
</div>
|
</div>
|
||||||
<hr class="my-4" />
|
<hr class="my-4 border-separator" />
|
||||||
<p class="text-sm font-medium pb-4">Completed</p>
|
<p class="text-sm font-medium pb-4">Completed</p>
|
||||||
<RadioGroup
|
<RadioGroup
|
||||||
name="completed"
|
name="completed"
|
||||||
@@ -273,7 +273,7 @@
|
|||||||
></Select>
|
></Select>
|
||||||
<button
|
<button
|
||||||
id="sort-order-btn"
|
id="sort-order-btn"
|
||||||
class="rounded-full py-1 px-[10px] hover:bg-gray-100 focus:ring-4 ring-gray-200 transition-colors"
|
class="btn-icon"
|
||||||
class:rotated={filter.sortOrder == "-"}
|
class:rotated={filter.sortOrder == "-"}
|
||||||
on:click={() => setSortOrder()}
|
on:click={() => setSortOrder()}
|
||||||
><i class="fa fa-arrow-up"></i></button
|
><i class="fa fa-arrow-up"></i></button
|
||||||
|
|||||||
BIN
web/static/models/earth2.glb
Normal file
BIN
web/static/models/earth2.glb
Normal file
Binary file not shown.
@@ -4,7 +4,22 @@ export default {
|
|||||||
theme: {
|
theme: {
|
||||||
extend: {
|
extend: {
|
||||||
colors: {
|
colors: {
|
||||||
'primary': '#242734',
|
'primary': 'rgba(var(--primary))',
|
||||||
|
'primary-hover': 'rgba(var(--primary-hover))',
|
||||||
|
'secondary-hover': 'rgba(var(--secondary-hover))',
|
||||||
|
|
||||||
|
'input-background': 'rgba(var(--input-background))',
|
||||||
|
'input-border': 'rgba(var(--input-border))',
|
||||||
|
'input-border-focus': 'rgba(var(--input-border-focus))',
|
||||||
|
|
||||||
|
'menu-background': 'rgba(var(--menu-background))',
|
||||||
|
'menu-item-background-hover': 'rgba(var(--menu-item-background-hover))',
|
||||||
|
'menu-item-background-focus': 'rgba(var(--menu-item-background-focus))',
|
||||||
|
|
||||||
|
'footer-background': 'rgba(var(--footer-background))',
|
||||||
|
|
||||||
|
'separator': 'rgba(var(--separator))',
|
||||||
|
|
||||||
},
|
},
|
||||||
fontFamily: {
|
fontFamily: {
|
||||||
'sans': ['IBMPlexSans', 'sans'],
|
'sans': ['IBMPlexSans', 'sans'],
|
||||||
|
|||||||
Reference in New Issue
Block a user