From 35872d731a0888c846018ac236e49312ed434a9f Mon Sep 17 00:00:00 2001 From: Alexander Bocken Date: Fri, 22 May 2026 18:33:23 +0200 Subject: [PATCH] feat(hikes): SBB-style public-transport journey planner for hike prose MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add JourneyPlanner.svelte: a "Von / Nach" connections widget to drop into a hike's prose (e.g. with a fixed trailhead destination) so readers can plan the trip there by public transport. Backed by the free Swiss transport API (transport.opendata.ch — the same one sbb-tui uses). - From/To fields: prefillable + per-field lockable (fromFixed / toFixed), swap when both editable, and "Aktueller Standort" via geolocation (coarse accuracy → fast on laptops; resolves to the nearest stop). - Station typeahead on both fields via /locations?type=station, so routes start/end at canonical stops instead of fuzzy-geocoded points (which was yielding roundabout connections); plus resolve-on-search fallback. The dropdown grows flush from the field as a route-line spur (matching dots), typed text highlighted. - Separate date + time controls: date defaults to the next weekend day (Sat on weekdays, Sun on a Saturday); time + departure/arrival target are author-settable props (time="08:00" target="arrival") and reader-editable. - Results: per-leg transport-type icons (bus/train/tram/ship/cable) and an expandable full itinerary (stations, times, platforms, line + direction, coloured rails, walk connectors); link to the full search.ch timetable. Add TimePicker.svelte: a shared pill time picker (HH:MM string, chevron nudges, hour/minute dropdown) in the same language as DatePicker / DateTimePicker. --- package.json | 2 +- src/lib/components/TimePicker.svelte | 326 +++++ .../components/hikes/JourneyPlanner.svelte | 1189 +++++++++++++++++ 3 files changed, 1516 insertions(+), 1 deletion(-) create mode 100644 src/lib/components/TimePicker.svelte create mode 100644 src/lib/components/hikes/JourneyPlanner.svelte diff --git a/package.json b/package.json index 54c23986..4aeaa078 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "homepage", - "version": "1.83.0", + "version": "1.84.0", "private": true, "type": "module", "scripts": { diff --git a/src/lib/components/TimePicker.svelte b/src/lib/components/TimePicker.svelte new file mode 100644 index 00000000..66c64f79 --- /dev/null +++ b/src/lib/components/TimePicker.svelte @@ -0,0 +1,326 @@ + + +
+
+ + + +
+ + {#if open} + + {/if} +
+ + diff --git a/src/lib/components/hikes/JourneyPlanner.svelte b/src/lib/components/hikes/JourneyPlanner.svelte new file mode 100644 index 00000000..3cb3fd1a --- /dev/null +++ b/src/lib/components/hikes/JourneyPlanner.svelte @@ -0,0 +1,1189 @@ + + +
+
+ + +
0}> + {#if fromFixed} + + {#if fromCurrent}Aktueller Standort{:else}{fromText}{/if} + + {:else if fromCurrent} + + Aktueller Standort + + + {:else} + onFieldInput('from', e.currentTarget.value)} + onfocus={() => onFieldInput('from', fromText)} + /> + + {#if activeField === 'from' && suggestions.length > 0} +
    + {#each suggestions as s (s)} +
  • + +
  • + {/each} +
+ {/if} + {/if} +
+ +
+ {#if toFixed} + + {#if toCurrent}Aktueller Standort{:else}{toText}{/if} + + {:else if toCurrent} + + Aktueller Standort + + + {:else} + onFieldInput('to', e.currentTarget.value)} + onfocus={() => onFieldInput('to', toText)} + /> + + {#if activeField === 'to' && suggestions.length > 0} +
    + {#each suggestions as s (s)} +
  • + +
  • + {/each} +
+ {/if} + {/if} +
+ + {#if canSwap} + + {/if} +
+ +
+
+ + +
+ + +
+ + + + {#if error} +

{error}

+ {/if} + + {#if connections && connections.length > 0} +
    + {#each enriched as e, i (i)} +
  1. + + + {#if expanded === i} +
    + {#each e.steps as step, si (si)} + {#if step.kind === 'walk'} +
    +
    + {:else} + {@const Icon = step.Icon} +
    + +
    +
    + {step.dep.time} + {step.dep.station} + {#if step.dep.platform}Gl. {step.dep.platform}{/if} +
    +
    + + {#if step.headsign}Richtung {step.headsign}{/if} +
    +
    + {step.arr.time} + {step.arr.station} + {#if step.arr.platform}Gl. {step.arr.platform}{/if} +
    +
    +
    + {/if} + {/each} +
    + {/if} +
  2. + {/each} +
+ {#if searchChLink} + + Ganzer Fahrplan + {/if} +

Verbindungen: transport.opendata.ch

+ {/if} +
+ +