Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 14 additions & 14 deletions src/App.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
import type { ProviderFactory, Provider, Weather } from './providers/Provider';
import { Location } from './providers/Location';
import { ExampleProvider } from './providers/ExampleProvider';
import { AutoExpand, configuration } from './Configuration';
import { AutoExpand, configuration, Layout } from './Configuration';

import Timestamp from './components/scalars/Timestamp.svelte';
import CurrentDetails from './components/CurrentDetails.svelte';
Expand Down Expand Up @@ -128,11 +128,12 @@

<div class="container mx-auto">
{#if weather}
{@const { sunset_timestamp, sunrise_timestamp, precipitation_amount, precipitation_probability } = weather.daily[0]}
<div class="mb-6">
<CurrentDetails current={weather.current} />
<CurrentDetails current={weather.current} suntimes={{ sunset_timestamp, sunrise_timestamp }} />
</div>
<div class="mx-6 mb-6">
<HourlyDetails hourly={weather.current.hourly} />
<HourlyDetails hourly={weather.current.hourly} today />
</div>
<Accordion
multiple
Expand All @@ -142,7 +143,7 @@
>
{#each weather.daily as daily, i}
<AccordionItem
class="!px-2 !py-3 md:!p-4"
class="!px-2 md:!px-4 py-4 md:px-8"
paddingDefault="py-4 px-4 md:px-14"
open={$configuration.autoexpand === AutoExpand.All || (i === 0 && $configuration.autoexpand === AutoExpand.Today)}
>
Expand All @@ -153,16 +154,15 @@
global_low={Math.min(...weather.daily.map((d) => d.temperature_low))}
global_high={Math.max(...weather.daily.map((d) => d.temperature_high))}
/>
<div class="grid gap-y-4 mb-2">
<DailyDetails {daily} />
<HourlyDetails hourly={daily.hourly} />
{#if daily.hourly[0].precipitation_probability !== undefined && $configuration.showHourlyPrecipitation}
<HourlyPrecipitationChart index={i} hourly={[...daily.hourly, ...(weather.daily[i + 1] ? [weather.daily[i + 1].hourly[0]] : [])]} />
{/if}
{#if daily.hourly[0].wind_speed !== undefined && $configuration.showHourlyWind}
<HourlyWindChart index={i} hourly={[...daily.hourly, ...(weather.daily[i + 1] ? [weather.daily[i + 1].hourly[0]] : [])]} />
{/if}
</div>
<DailyDetails {daily} />
<br />
<HourlyDetails hourly={daily.hourly} />
{#if daily.hourly[0].precipitation_probability !== undefined && $configuration.showHourlyPrecipitation}
<HourlyPrecipitationChart index={i} hourly={[...daily.hourly, ...(weather.daily[i + 1] ? [weather.daily[i + 1].hourly[0]] : [])]} />
{/if}
{#if daily.hourly[0].wind_speed !== undefined && $configuration.showHourlyWind}
<HourlyWindChart index={i} hourly={[...daily.hourly, ...(weather.daily[i + 1] ? [weather.daily[i + 1].hourly[0]] : [])]} />
{/if}
</AccordionItem>
{/each}
</Accordion>
Expand Down
13 changes: 12 additions & 1 deletion src/Configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ export enum AutoExpand {
None,
}

export enum Layout {
Horizontal,
Vertical,
}

export interface Configuration {
providerFactory: ProviderFactory;
providerParams: { [key: string]: string };
Expand All @@ -31,6 +36,7 @@ export interface Configuration {
refreshInterval: number;
showHourlyPrecipitation: boolean;
showHourlyWind: boolean;
layout: Layout;
}

const DEFAULT_CONFIGURATION: Configuration = {
Expand All @@ -43,6 +49,7 @@ const DEFAULT_CONFIGURATION: Configuration = {
refreshInterval: 2 * 3600,
showHourlyPrecipitation: true,
showHourlyWind: true,
layout: Layout.Horizontal,
};

function decodeConfiguration(params: { [key: string]: string }): Configuration {
Expand All @@ -63,6 +70,7 @@ function decodeConfiguration(params: { [key: string]: string }): Configuration {
const showHourlyPrecipitation =
params['hourly_precipitation'] === undefined ? DEFAULT_CONFIGURATION.showHourlyPrecipitation : params['hourly_precipitation'] === 'true' ? true : false;
const showHourlyWind = params['hourly_wind'] === undefined ? DEFAULT_CONFIGURATION.showHourlyWind : params['hourly_wind'] === 'true' ? true : false;
const layout = params['layout'] === 'vertical' ? Layout.Vertical : Layout.Horizontal;

return {
providerFactory,
Expand All @@ -74,6 +82,7 @@ function decodeConfiguration(params: { [key: string]: string }): Configuration {
refreshInterval,
showHourlyPrecipitation,
showHourlyWind,
layout,
};
}

Expand Down Expand Up @@ -107,7 +116,9 @@ function encodeConfiguration(configuration: Configuration): object {
if (configuration.showHourlyWind !== DEFAULT_CONFIGURATION.showHourlyWind) {
params['hourly_wind'] = configuration.showHourlyWind.toString();
}

if (configuration.layout !== DEFAULT_CONFIGURATION.layout) {
params['layout'] = configuration.layout === Layout.Vertical ? 'vertical' : 'horizontal';
}
return params;
}

Expand Down
16 changes: 15 additions & 1 deletion src/SettingsModal.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import { ProviderFactories } from './providers';
import { Location } from './providers/Location';
import { Units, AutoExpand, loadConfiguration, storeConfiguration } from './Configuration';
import { Units, AutoExpand, loadConfiguration, Layout, storeConfiguration } from './Configuration';

import { Modal, Tabs, TabItem, Label, Select, Input, Hr, Button, ButtonGroup, Spinner, Radio, Checkbox } from 'flowbite-svelte';
import Icon from '@iconify/svelte';
Expand All @@ -29,6 +29,7 @@
let showHourlyPrecipitation: boolean;
let showHourlyWind: boolean;
let valid: boolean;
let layout: Layout;

/* Loading State for location */
let locationLoading: boolean = false;
Expand All @@ -51,6 +52,7 @@
title = currentConfiguration.title;
showHourlyPrecipitation = currentConfiguration.showHourlyPrecipitation;
showHourlyWind = currentConfiguration.showHourlyWind;
layout = currentConfiguration.layout;

updateProviderParams();

Expand Down Expand Up @@ -88,6 +90,7 @@
refreshInterval: currentConfiguration.refreshInterval,
showHourlyPrecipitation,
showHourlyWind,
layout,
};

storeConfiguration(configuration);
Expand Down Expand Up @@ -203,6 +206,17 @@
bind:value={autoexpand}
/>
</div>
<div>
<Label for="select-layout" class="mb-2">Layout</Label>
<Select
id="select-layout"
items={[
{ name: 'Horizontal', value: Layout.Horizontal },
{ name: 'Vertical', value: Layout.Vertical },
]}
bind:value={layout}
/>
</div>
<div>
<Checkbox bind:checked={showHourlyPrecipitation}>Show Hourly Precipitation Chart</Checkbox>
</div>
Expand Down
6 changes: 5 additions & 1 deletion src/components/CurrentDetails.svelte
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script lang="ts">
import type { CurrentWeather } from '../providers/Provider';
import type { CurrentWeather, SunTimes } from '../providers/Provider';

import Conditions from './scalars/Conditions.svelte';
import ConditionsIcon from './scalars/ConditionsIcon.svelte';
Expand All @@ -9,8 +9,10 @@
import UVIndex from './scalars/UVIndex.svelte';
import Distance from './scalars/Distance.svelte';
import Pressure from './scalars/Pressure.svelte';
import DailySunDetails from './DailySunDetails.svelte';

export let current: CurrentWeather;
export let suntimes: SunTimes;
</script>

<div>
Expand All @@ -36,5 +38,7 @@
<div><span class="font-semibold">Visibility: </span><Distance value={current.visibility} /></div>
{/if}
<div><span class="font-semibold">Pressure: </span><Pressure value={current.pressure} /></div>
<DailySunDetails {suntimes} />
<!-- <DailyPrecipitationDetails {precipitation} /> -->
</div>
</div>
26 changes: 26 additions & 0 deletions src/components/DailyPrecipitationDetails.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<script lang="ts">
import type { DailyPrecipitation } from '../providers/Provider';

import Amount from './scalars/Amount.svelte';

export let precipitation: DailyPrecipitation;
</script>

{#if precipitation.precipitation_probability !== undefined && precipitation.precipitation_amount !== undefined}
<div>
<span class="text-sm md:text-base font-semibold">Precipitation:</span> <span class="text-sm md:text-base">{precipitation.precipitation_probability}%</span>
</div>
<div>
<span class="text-sm md:text-base font-semibold">Amount:</span>
<Amount value={precipitation.precipitation_amount} />
</div>
{:else if precipitation.precipitation_probability !== undefined}
<div>
<span class="text-sm md:text-base font-semibold">Precipitation:</span> <span class="text-sm md:text-base">{precipitation.precipitation_probability}%</span>
</div>
{:else if precipitation.precipitation_amount !== undefined}
<div>
<span class="text-sm md:text-base font-semibold">Precipitation:</span>
<span class="text-sm md:text-base"><Amount value={precipitation.precipitation_amount} /></span>
</div>
{/if}
18 changes: 18 additions & 0 deletions src/components/DailySunDetails.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<script lang="ts">
import type { SunTimes } from '../providers/Provider';

import Icon from '@iconify/svelte';

import Timestamp from './scalars/Timestamp.svelte';

export let suntimes: SunTimes;
</script>

<div>
<span style="transform:translate(-2px,-2px)"><Icon icon="mingcute:sunrise-line" class="inline text-2xl sm:text-3xl align-bottom" /></span>
<span class="font-semibold">Sunrise: </span><Timestamp format="short" value={suntimes.sunrise_timestamp} />
</div>
<div>
<span style="transform:translate(-2px,-2px)"><Icon icon="mingcute:sunset-fill" class="inline text-2xl sm:text-3xl align-bottom" /></span>
<span class="font-semibold">Sunset: </span><Timestamp format="short" value={suntimes.sunset_timestamp} />
</div>
68 changes: 10 additions & 58 deletions src/components/HourlyDetails.svelte
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
<script lang="ts">
import { ConditionsIcon } from '../providers/Provider';
import type { HourlyWeather } from '../providers/Provider';
import { configuration, Layout } from '../Configuration';

import HourlyVerticalLayout from './HourlyVerticalLayout.svelte';
import HourlyHorizontalLayout from './HourlyHorizontalLayout.svelte';
import Temperature from './scalars/Temperature.svelte';
import Timestamp from './scalars/Timestamp.svelte';

/* Properties */

export let hourly: HourlyWeather[] = [];
export let today: boolean = false;

/* Constants */

Expand Down Expand Up @@ -62,8 +66,6 @@
[ConditionsIcon.Thunderstorm]: HourlyConditions.Rain,
};

const MIN_TEMPERATURE_OPACITY = 0.25;

/* State */

let aggregation: {
Expand All @@ -72,12 +74,7 @@
}[] = [];
let temperatureLow: number;
let temperatureHigh: number;

function temperatureOpacity(value: number): string {
let percent = (value - temperatureLow) / (temperatureHigh - temperatureLow);
const opacity = percent * (1 - MIN_TEMPERATURE_OPACITY) + MIN_TEMPERATURE_OPACITY;
return opacity.toFixed(2);
}
let selectedPill = 'Temp';

$: {
/* Aggregate conditions into contiguous regions */
Expand All @@ -97,53 +94,8 @@
}
</script>

<div class="-mb-2">
<div class="flex mb-1.5 overflow-hidden rounded-md text-sm sm:text-base">
{#each aggregation as entry}
<div class="h-10 leading-9 {CLASS_TEXT_MAP[entry.conditions][0]} text-center" style="width: {(100 * entry.duration) / 24}%;">
{#if entry.duration > 4}
<div class="truncate">{CLASS_TEXT_MAP[entry.conditions][1]}</div>
{:else if entry.duration > 2}
<div class="hidden md:block truncate">{CLASS_TEXT_MAP[entry.conditions][1]}</div>
{/if}
</div>
{/each}
</div>
<div class="flex w-full mb-1">
{#each Array(25) as _, i}
<div class="{i % 2 === 0 ? 'h-[8px]' : 'h-[5px]'} border-l border-gray-400" style="width: {i < 24 ? 100 / 24 : 0}%;" />
{/each}
</div>
<div class="flex w-full text-xs sm:text-sm">
<div style="width: {100 / 24}%;">
<div class="hidden md:block">
<Timestamp value={hourly[0].timestamp} format="hour" />
</div>
</div>
{#each Array(11) as _, i}
{@const timestamp = hourly[2 * (i + 1)].timestamp}
<div class="text-center" style="width: {100 / 12}%;">
<div class="{i === 0 || i === 2 || i === 4 || i === 6 || i === 8 || i === 10 ? 'block' : 'hidden'} md:block">
<Timestamp value={timestamp} format="hour" />
</div>
</div>
{/each}
<div style="width: {100 / 24}%;" />
</div>
<div class="flex w-full text-base sm:text-lg font-light text-black dark:text-white">
<div style="width: {100 / 24}%; opacity: {temperatureOpacity(hourly[0].temperature)};">
<div class="hidden md:block">
<Temperature value={hourly[0].temperature} />
</div>
</div>
{#each Array(11) as _, i}
{@const temperature = hourly[2 * (i + 1)].temperature}
<div class="text-center" style="width: {100 / 12}%; opacity: {temperatureOpacity(temperature)};">
<div class="{i === 0 || i === 2 || i === 4 || i === 6 || i === 8 || i === 10 ? 'block' : 'hidden'} md:block">
&nbsp;<Temperature value={temperature} />
</div>
</div>
{/each}
<div style="width: {100 / 24}%;" />
</div>
</div>
{#if $configuration.layout === Layout.Horizontal}
<HourlyHorizontalLayout {aggregation} {hourly} {temperatureLow} {temperatureHigh} {CLASS_TEXT_MAP} />
{:else if $configuration.layout === Layout.Vertical}
<HourlyVerticalLayout {aggregation} {today} {hourly} {temperatureLow} {temperatureHigh} {CLASS_TEXT_MAP} />
{/if}
Loading