adds trails page

This commit is contained in:
Christian Beutel
2024-02-04 22:48:04 +01:00
parent c370690747
commit ec33723bdc
19 changed files with 799 additions and 30 deletions

View File

@@ -0,0 +1,54 @@
<script lang="ts">
import * as noUiSlider from "noUiSlider";
import "nouislider/dist/nouislider.css";
import { onMount } from "svelte";
export let minValue = 0;
export let maxValue = 100;
export let currentMin = minValue;
export let currentMax = maxValue;
let sliderContainer: any;
onMount(() => {
const updateValues = (values: string[]) => {
currentMin = parseFloat(values[0]);
currentMax = parseFloat(values[1]);
};
noUiSlider.create(sliderContainer, {
start: [currentMin, currentMax],
connect: true,
range: {
min: minValue,
max: maxValue,
},
});
sliderContainer.noUiSlider.on("update", updateValues);
});
</script>
<div class="my-4" id="slider" bind:this={sliderContainer}></div>
<style>
:global(.noUi-horizontal) {
height: 6px;
}
:global(.noUi-connect) {
@apply bg-primary;
}
:global(.noUi-horizontal .noUi-handle) {
@apply rounded-full w-7 h-7 -top-3;
}
:global(.noUi-handle::before) {
@apply content-none;
}
:global(.noUi-handle::after) {
@apply content-none;
}
</style>

View File

@@ -30,7 +30,7 @@
<button class="flex items-center justify-center" on:click={toggleMenu} type="button">
<slot>
<i
class="fa fa-ellipsis-vertical text-{size} hover:bg-gray-300 px-[14px] py-2 rounded-full hover:bg-opacity-50"
class="fa fa-ellipsis-vertical text-{size} hover:bg-gray-300 hover:bg-opacity-50 px-[14px] py-2 rounded-full "
></i>
</slot>
</button>

View File

@@ -0,0 +1,41 @@
<script context="module" lang="ts">
export type RadioItem = {
text: string;
value: string;
icon?: string;
};
</script>
<script lang="ts">
import { createEventDispatcher } from "svelte";
export let items: RadioItem[];
export let name: string;
export let selected: number = 0;
const dispatch = createEventDispatcher();
function handleRadioChange(radioIndex: number) {
selected = radioIndex;
dispatch('change', items[selected])
}
</script>
{#each items as item, i}
<div class="flex items-center mb-4">
<input
id="{name}-radio-{i}"
name="{name}-radio"
type="radio"
checked={i == selected}
value={item.value}
class="w-4 h-4 text-primary bg-gray-100 border-gray-300 focus:ring-gray-400 focus:ring-2"
on:change={() => handleRadioChange(i)}
/>
<label
for="{name}-radio-{i}"
class="ms-2 text-sm text-gray-900 dark:text-gray-300"
>{item.text}</label
>
</div>
{/each}

View File

@@ -0,0 +1,110 @@
<script context="module" lang="ts">
export type SearchItem = {
text: string;
description?: string;
value: any;
icon: string;
};
</script>
<script lang="ts">
import { createEventDispatcher } from "svelte";
import { fade } from "svelte/transition";
import TextField from "./text_field.svelte";
export let maxSearchLength: number = 5;
export let value: string = "";
export let items: SearchItem[] = [];
export let placeholder: string = "Search...";
export let large: boolean = false;
const dispatch = createEventDispatcher();
let lastSearch: string = "";
let searching: boolean = false;
let typingTimer!: any;
$: dropDownOpen = value.length > 0 && items.length > 0 && searching;
function onSearchType() {
clearTimeout(typingTimer);
if (Math.abs(value.length - lastSearch.length) > maxSearchLength) {
update(value);
return;
}
typingTimer = setTimeout(() => {
update(value);
}, 500);
}
function update(q: string) {
lastSearch = q;
dispatch("update", q);
}
function handleItemClick(item: SearchItem) {
searching = false;
dispatch("click", item);
}
function clear() {
value = "";
update(value);
}
</script>
<div class="relative text-gray-600">
<span class="absolute top-1/2 -translate-y-1/2 left-0 pl-4">
<i class="fa fa-search" class:text-2xl={large}></i>
</span>
{#if value.length > 0}
<button
class="absolute top-1/2 -translate-y-1/2 right-0 h-6 w-6 mr-4 hover:bg-gray-300 hover:bg-opacity-50 rounded-full"
on:click={clear}
in:fade={{ duration: 150 }}
out:fade={{ duration: 150 }}
>
<i class="fa fa-close text-sm"></i>
</button>
{/if}
<TextField
type="search"
name="q"
autocomplete="off"
extraClasses="{large ? 'pl-14 text-2xl min-w-80 w-[33vw] max-w-[532px]' : 'pl-10'}"
{placeholder}
bind:value
on:input={onSearchType}
on:focusin={() => (searching = true)}
></TextField>
{#if dropDownOpen}
<ul
class="menu absolute bg-white border rounded-xl shadow-md overflow-hidden text-black w-full"
class:none={!dropDownOpen}
style="z-index: 1001"
>
{#each items as item}
<!-- svelte-ignore a11y-no-noninteractive-element-interactions -->
<!-- svelte-ignore a11y-no-noninteractive-tabindex -->
<li
class="menu-item flex items-center px-4 py-3 cursor-pointer hover:bg-gray-100 focus:bg-gray-200 transition-colors"
tabindex="0"
on:mouseup|stopPropagation={() => handleItemClick(item)}
on:keydown|stopPropagation={() => handleItemClick(item)}
>
<i class="fa fa-{item.icon} mr-6"></i>
<div>
<p>{item.text}</p>
{#if item.description}
<p class="text-sm text-gray-500">
{item.description}
</p>
{/if}
</div>
</li>
{/each}
</ul>
{/if}
</div>

View File

@@ -0,0 +1,52 @@
<script lang="ts">
import noUiSlider from "noUiSlider";
import "nouislider/dist/nouislider.css";
import { onMount } from "svelte";
export let minValue = 0;
export let maxValue = 100;
export let currentValue = maxValue / 2;
let sliderContainer: any;
onMount(() => {
const updateValues = (values: string[]) => {
currentValue = parseFloat(values[0]);
};
noUiSlider.create(sliderContainer, {
start: currentValue,
connect: [true, false],
range: {
min: minValue,
max: maxValue,
},
});
sliderContainer.noUiSlider.on("update", updateValues);
});
</script>
<div class="my-4" id="slider" bind:this={sliderContainer}></div>
<style>
:global(.noUi-horizontal) {
height: 6px;
}
:global(.noUi-connect) {
@apply bg-primary;
}
:global(.noUi-horizontal .noUi-handle) {
@apply rounded-full w-7 h-7 -top-3;
}
:global(.noUi-handle::before) {
@apply content-none;
}
:global(.noUi-handle::after) {
@apply content-none;
}
</style>

View File

@@ -6,8 +6,8 @@
export let error: string = "";
export let icon: string = "";
export let extraClasses: string = "";
export let type: "text" | "password" = "text";
export let type: "text" | "password" | "search" = "text";
export let autocomplete: "on" | "off" = "on"
function typeAction(node: HTMLInputElement) {
node.type = type;
}
@@ -28,9 +28,13 @@
class="bg-gray-50 border rounded-md p-3 transition-colors focus:border-primary focus:outline-none focus:ring-0 w-full {extraClasses}"
class:border-red-400={error.length > 0}
class:bg-red-50={error.length > 0}
{autocomplete}
use:typeAction
bind:value
on:change
on:input
on:focusin
on:focusout
{placeholder}
/>
</div>