feat: increase button size while driving
Some checks failed
TrafficCue CI / check (push) Failing after 1m59s
TrafficCue CI / build (push) Successful in 9m50s
TrafficCue CI / build-android (push) Successful in 27m5s

This commit is contained in:
2025-10-15 17:24:54 +02:00
parent 60e074a70f
commit c3dbbf2637
19 changed files with 195 additions and 21 deletions

View File

@@ -21,6 +21,8 @@
import Input from "../ui/input/input.svelte";
import EvConnectorSelect from "./EVConnectorSelect.svelte";
import { m } from "$lang/messages";
import { localStore } from "$lib/services/localStore.svelte";
import { isDriving } from "./location.svelte";
let open = $state(false);
@@ -52,11 +54,14 @@
fuelType: "diesel",
preferredFuel: "Diesel",
});
const biggerButtonsInDrive = localStore<boolean>("bigger-buttons-in-drive", false);
const shouldUseLargeSize = $derived(biggerButtonsInDrive.current ? isDriving() : false);
</script>
<Drawer.Root bind:open>
<Drawer.Trigger
class={buttonVariants({ variant: "secondary", class: "w-full p-5" })}
class={buttonVariants({ variant: "secondary", class: "w-full", size: shouldUseLargeSize ? "drive" : "default" })}
>
{@render children()}
</Drawer.Trigger>

View File

@@ -45,6 +45,7 @@
"nearby-poi": "NearbyPOISidebar",
licenses: "settings/LicensesSidebar",
calendar: "settings/CalendarSidebar",
appearance: "settings/AppearanceSidebar",
};
const fullscreen: Record<string, boolean> = {
@@ -64,6 +65,7 @@
"nearby-poi": false,
licenses: true,
calendar: true,
appearance: true,
};
let isDragging = false;

View File

@@ -19,6 +19,8 @@
import AddVehicleDrawer from "./AddVehicleDrawer.svelte";
import { m } from "$lang/messages";
import { updateStore } from "$lib/services/stores.svelte";
import { localStore } from "$lib/services/localStore.svelte";
import { isDriving } from "./location.svelte";
let open = $state(false);
@@ -38,11 +40,14 @@
return TractorIcon; // Default icon if no match
}
}
const biggerButtonsInDrive = localStore<boolean>("bigger-buttons-in-drive", false);
const shouldUseLargeSize = $derived(biggerButtonsInDrive.current ? isDriving() : false);
</script>
<Drawer.Root bind:open>
<Drawer.Trigger
class={buttonVariants({ variant: "secondary", class: "w-full p-5" })}
class={buttonVariants({ variant: "secondary", class: "w-full", size: shouldUseLargeSize ? "drive" : "default" })}
>
{@const vehicle = selectedVehicle()?.data ?? DefaultVehicle}
{@const Icon = getVehicleIcon(vehicle.type)}
@@ -63,7 +68,7 @@
variant={selectedVehicle()?.data === vehicle.data
? "default"
: "secondary"}
class="w-[calc(100%-48px-8px)] p-5"
class="w-[calc(100%-48px-8px)]"
onclick={() => {
selectVehicle(vehicle.data);
open = false;
@@ -92,10 +97,8 @@
{/each}
<AddVehicleDrawer>
<Button variant="secondary" class="w-full p-5">
<PlusCircleIcon />
{m["vehicles.selector.add"]()}
</Button>
<PlusCircleIcon />
{m["vehicles.selector.add"]()}
</AddVehicleDrawer>
</div>
</Drawer.Content>

View File

@@ -33,6 +33,12 @@ export const location = $state({
lastUpdate: null as Date | null,
});
const _isDriving = $derived(location.speed > (7 / 3.6));
export function isDriving() {
return _isDriving;
}
export function watchLocation() {
if (navigator.geolocation) {
navigator.geolocation.watchPosition(

View File

@@ -22,7 +22,6 @@
>
<Button
variant="secondary"
size="lg"
style="flex: 1;"
onclick={() => {
view.switch("nearby-poi", {
@@ -36,7 +35,6 @@
</Button>
<Button
variant="secondary"
size="lg"
style="flex: 1;"
onclick={() => {
view.switch("nearby-poi", {

View File

@@ -12,10 +12,8 @@
</h1>
<AddVehicleDrawer>
<Button variant="secondary" class="w-full p-5">
<PlusCircleIcon />
{m["vehicles.selector.add"]()}
</Button>
<PlusCircleIcon />
{m["vehicles.selector.add"]()}
</AddVehicleDrawer>
<Button

View File

@@ -0,0 +1,11 @@
<script>
import { m } from "$lang/messages";
import SidebarHeader from "../SidebarHeader.svelte";
import SettingsToggle from "./SettingsToggle.svelte";
</script>
<SidebarHeader>
{m["sidebar.appearance.header"]()}
</SidebarHeader>
<SettingsToggle text={m["sidebar.appearance.bigger-buttons-in-drive"]()} localStorageKey="bigger-buttons-in-drive" />

View File

@@ -24,7 +24,7 @@
<Button
variant="secondary"
style="width: 100%; height: 40px;"
style="width: 100%;"
{disabled}
onclick={() => {
if (viewName) view.switch(viewName);

View File

@@ -25,6 +25,11 @@
text={m["sidebar.language.header"]()}
view="language"
/>
<SettingsButton
icon={PaintbrushIcon}
text={m["sidebar.appearance.header"]()}
view="appearance"
/>
</section>
<section>

View File

@@ -0,0 +1,42 @@
<script lang="ts">
import type { IconProps } from "@lucide/svelte";
import type { Component } from "svelte";
import Switch from "$lib/components/ui/switch/switch.svelte";
import { Label } from "$lib/components/ui/label";
import { eventTarget } from "$lib/services/localStore.svelte";
let {
icon: Icon,
text,
onchange,
disabled,
localStorageKey,
value = $bindable(localStorageKey ? localStorage.getItem(localStorageKey) === "true" : false),
}: {
icon?: Component<IconProps>;
text: string;
onchange?: () => void;
disabled?: boolean;
value?: boolean;
localStorageKey?: string;
} = $props();
</script>
<div class="flex">
<Label
style="width: 100%;"
for="settings-toggle"
>
{#if Icon}
<Icon />
{/if}
{text}
</Label>
<Switch {disabled} bind:checked={value} onCheckedChange={() => {
if (onchange) onchange();
if (localStorageKey) {
localStorage.setItem(localStorageKey, value ? "true" : "false");
eventTarget.dispatchEvent(new CustomEvent("localStorageChanged", { detail: { key: localStorageKey, value } }));
}
}} id="settings-toggle" />
</div>

View File

@@ -1,4 +1,6 @@
<script lang="ts" module>
import { isDriving } from "$lib/components/lnv/location.svelte";
import { localStore } from "$lib/services/localStore.svelte";
import { cn, type WithElementRef } from "$lib/utils.js";
import type {
HTMLAnchorAttributes,
@@ -6,6 +8,9 @@
} from "svelte/elements";
import { type VariantProps, tv } from "tailwind-variants";
const biggerButtonsInDrive = localStore<boolean>("bigger-buttons-in-drive", false);
const shouldUseLargeSize = $derived(biggerButtonsInDrive.current ? isDriving() : false);
export const buttonVariants = tv({
base: "focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive inline-flex shrink-0 items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium outline-none transition-all focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0",
variants: {
@@ -27,6 +32,7 @@
sm: "h-8 gap-1.5 rounded-md px-3 has-[>svg]:px-2.5",
lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
icon: "size-9",
drive: "h-12 rounded-md px-8 has-[>svg]:px-6"
},
},
defaultVariants: {
@@ -63,7 +69,7 @@
<a
bind:this={ref}
data-slot="button"
class={cn(buttonVariants({ variant, size }), className)}
class={cn(buttonVariants({ variant, size: shouldUseLargeSize ? "drive" : size }), className)}
href={disabled ? undefined : href}
aria-disabled={disabled}
role={disabled ? "link" : undefined}
@@ -76,7 +82,7 @@
<button
bind:this={ref}
data-slot="button"
class={cn(buttonVariants({ variant, size }), className)}
class={cn(buttonVariants({ variant, size: shouldUseLargeSize ? "drive" : size }), className)}
{type}
{disabled}
{...restProps}

View File

@@ -0,0 +1,7 @@
import Root from "./label.svelte";
export {
Root,
//
Root as Label,
};

View File

@@ -0,0 +1,20 @@
<script lang="ts">
import { Label as LabelPrimitive } from "bits-ui";
import { cn } from "$lib/utils.js";
let {
ref = $bindable(null),
class: className,
...restProps
}: LabelPrimitive.RootProps = $props();
</script>
<LabelPrimitive.Root
bind:ref
data-slot="label"
class={cn(
"flex select-none items-center gap-2 text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-50 group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50",
className
)}
{...restProps}
/>

View File

@@ -0,0 +1,7 @@
import Root from "./switch.svelte";
export {
Root,
//
Root as Switch,
};

View File

@@ -0,0 +1,29 @@
<script lang="ts">
import { Switch as SwitchPrimitive } from "bits-ui";
import { cn, type WithoutChildrenOrChild } from "$lib/utils.js";
let {
ref = $bindable(null),
class: className,
checked = $bindable(false),
...restProps
}: WithoutChildrenOrChild<SwitchPrimitive.RootProps> = $props();
</script>
<SwitchPrimitive.Root
bind:ref
bind:checked
data-slot="switch"
class={cn(
"data-[state=checked]:bg-primary data-[state=unchecked]:bg-input focus-visible:border-ring focus-visible:ring-ring/50 dark:data-[state=unchecked]:bg-input/80 shadow-xs peer inline-flex h-[1.15rem] w-8 shrink-0 items-center rounded-full border border-transparent outline-none transition-all focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50",
className
)}
{...restProps}
>
<SwitchPrimitive.Thumb
data-slot="switch-thumb"
class={cn(
"bg-background dark:data-[state=unchecked]:bg-foreground dark:data-[state=checked]:bg-primary-foreground pointer-events-none block size-4 rounded-full ring-0 transition-transform data-[state=checked]:translate-x-[calc(100%-2px)] data-[state=unchecked]:translate-x-0"
)}
/>
</SwitchPrimitive.Root>

View File

@@ -0,0 +1,29 @@
import type { WrappedValue } from "$lib/services/stores.svelte";
export const eventTarget = new EventTarget();
export function localStore<T>(key: string, defaultValue: T): WrappedValue<T> {
const storedValue = localStorage.getItem(key);
const initialValue = storedValue !== null ? JSON.parse(storedValue) : defaultValue;
const state = $state<WrappedValue<T>>({ current: initialValue });
eventTarget.addEventListener("localStorageChanged", (event) => {
const customEvent = event as CustomEvent;
console.log("localStorageChanged event received", customEvent.detail);
if(customEvent.detail.key === key) {
const newValue = customEvent.detail.value as T;
console.log(`localStore: ${key} updated from another tab`, newValue);
if(JSON.stringify(state.current) === JSON.stringify(newValue)) return;
state.current = newValue;
}
});
return {
get current() {
return state.current;
},
set current(newValue: T) {
state.current = newValue;
eventTarget.dispatchEvent(new CustomEvent("localStorageChanged", { detail: { key, value: newValue } }));
localStorage.setItem(key, JSON.stringify(newValue));
},
};
}