new stuff
This commit is contained in:
10 files changed
+3447
-68
No files matched your search
+8
-2
@@ -84,7 +84,7 @@ export function stepsValidForStationPair(steps, stationA, stationB) {
|
||||
if (steps[steps.length - 1].station != stationB)
|
||||
return [
|
||||
false,
|
||||
`Route doesn't end at ${agency.stations[stationB].stop_name}.`,
|
||||
[`Route doesn't end at ${agency.stations[stationB].stop_name}.`],
|
||||
];
|
||||
|
||||
let stops = 0;
|
||||
@@ -99,7 +99,13 @@ export function stepsValidForStationPair(steps, stationA, stationB) {
|
||||
if (!possible) {
|
||||
return [
|
||||
false,
|
||||
`${steps[i].line} doesn't serve ${agency.stations[err].stop_name}`,
|
||||
new Array(steps.length).fill(
|
||||
`${agency.routes[steps[i].line].route_long_name} doesn't serve ${
|
||||
agency.stations[err].stop_name
|
||||
}`,
|
||||
i,
|
||||
i + 1
|
||||
),
|
||||
];
|
||||
}
|
||||
stops += _stops;
|
||||
|
||||
+38
-12
@@ -1,5 +1,5 @@
|
||||
<script>
|
||||
import { onMount, createEventDispatcher } from "svelte";
|
||||
import { onMount, createEventDispatcher, tick } from "svelte";
|
||||
import agency, { minStops, randomStationPair, stepsValidForStationPair } from "../agency"
|
||||
import MbtaLine from "./MbtaLine.svelte";
|
||||
import RouteBuilder from "./RouteBuilder.svelte";
|
||||
@@ -17,52 +17,74 @@
|
||||
let to
|
||||
|
||||
let points = 0
|
||||
let errors = []
|
||||
|
||||
function setStationPair() {
|
||||
let pointFlourish
|
||||
let pointCounter
|
||||
let stationsElement
|
||||
|
||||
async function setStationPair() {
|
||||
const stations = randomStationPair()
|
||||
from = stations[0]
|
||||
to = stations[1]
|
||||
|
||||
await tick()
|
||||
|
||||
gsap.from(stationsElement, { opacity: 0, scale: 0.9 })
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
setStationPair()
|
||||
})
|
||||
|
||||
let pointFlourish
|
||||
let pointCounter
|
||||
let disabled = false
|
||||
|
||||
async function submit() {
|
||||
if(disabled) return
|
||||
|
||||
disabled = true
|
||||
|
||||
if(route[route.length - 1].station != to) {
|
||||
return alert(`Oops, your route doesn't end at ${agency.stations[to].stop_name}!`)
|
||||
}
|
||||
|
||||
const [valid, err, stops] = (stepsValidForStationPair(route, from, to))
|
||||
if(!valid) {
|
||||
gsap.to(pointCounter, {
|
||||
await gsap.to(pointCounter, {
|
||||
keyframes: {
|
||||
rotate: [0, -10, 10, -10, 10, -10, 10, -10, 10, 0]
|
||||
}
|
||||
})
|
||||
}).then()
|
||||
|
||||
errors = err
|
||||
} else if(stops > minStops(from, to)) {
|
||||
await addPoints(1)
|
||||
next()
|
||||
} else {
|
||||
await addPoints(2)
|
||||
next()
|
||||
}
|
||||
}
|
||||
|
||||
if(question >= 9) {
|
||||
function next() {
|
||||
if(question >= questionCount - 1) {
|
||||
window.umami?.track("Completed quiz")
|
||||
dispatch("done", points)
|
||||
return
|
||||
}
|
||||
|
||||
question++
|
||||
route = []
|
||||
errors = []
|
||||
setStationPair()
|
||||
|
||||
disabled = false
|
||||
}
|
||||
|
||||
async function addPoints(p) {
|
||||
points += p
|
||||
pointFlourish.innerText = `+${p}`
|
||||
gsap.to(pointFlourish, {x: -40, y: -50, rotate: -30, scale: 2, opacity: 0, duration: 1, startAt: {opacity: 1, y: 0, x: 0, scale: 1, rotate: 0}})
|
||||
await gsap.to(pointFlourish, {x: -40, y: -50, rotate: -30, scale: 2, opacity: 0, duration: 1, startAt: {opacity: 1, y: 0, x: 0, scale: 1, rotate: 0}}).then()
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -73,7 +95,7 @@
|
||||
</div>
|
||||
|
||||
{#if from && to}
|
||||
<h3 class="text-3xl">
|
||||
<h3 class="text-3xl inline-block" bind:this={stationsElement}>
|
||||
<span class="inline-flex items-center justify-center gap-2">
|
||||
<span class="font-bold">{agency.stations[from].stop_name}</span>
|
||||
{#each agency.stations[from].routes as line}
|
||||
@@ -94,8 +116,12 @@
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<RouteBuilder lines={agency.routes} stations={agency.stations} origin={agency.stations[from]?.stop_name} bind:route />
|
||||
<RouteBuilder {errors} lines={agency.routes} stations={agency.stations} origin={agency.stations[from]?.stop_name} showRemovalButton={!disabled} bind:route />
|
||||
|
||||
<button class="text-md border border-blue-500 text-blue-500 font-bold rounded-lg px-5 py-1 uppercase cursor-pointer transition-colors hover:bg-blue-50" on:click={submit} class:opacity-50={route.length == 0} disabled={route.length == 0}>
|
||||
Submit
|
||||
<button class="text-md border border-blue-500 text-blue-500 font-bold rounded-lg px-5 py-1 uppercase cursor-pointer transition-colors hover:bg-blue-50" on:click={() => errors.length == 0 ? submit() : next()} class:opacity-50={route.length == 0} disabled={route.length == 0}>
|
||||
{#if errors.length == 0}
|
||||
Submit
|
||||
{:else}
|
||||
Next
|
||||
{/if}
|
||||
</button>
|
||||
@@ -1,6 +1,7 @@
|
||||
<script>
|
||||
import Game from "./Game.svelte";
|
||||
import screenshot from "../screenshot.png"
|
||||
import Route from "./Route.svelte";
|
||||
|
||||
let page = "welcome"
|
||||
let score = 0
|
||||
@@ -28,6 +29,14 @@
|
||||
{/if}
|
||||
|
||||
<div class="flex flex-col items-center justify-center">
|
||||
<div class="mx-auto">
|
||||
<Route origin="Sullivan Square" steps={[
|
||||
{ line: "Orange", station: "place-north" },
|
||||
{ line: "Green-D", station: "place-pktrm" },
|
||||
{ line: "Red", station: "place-knncl" },
|
||||
]} />
|
||||
</div>
|
||||
|
||||
<div class="inline-block relative text-left mb-5">
|
||||
<h1 class="font-black text-5xl">The Train Quiz</h1>
|
||||
<div class="max-w-72">
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
<script>
|
||||
import MbtaLine from "./MbtaLine.svelte";
|
||||
import agency from "../agency"
|
||||
import { createEventDispatcher } from "svelte";
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
export let steps = [];
|
||||
export let origin;
|
||||
export let showRemovalButton = false;
|
||||
export let errors = []
|
||||
</script>
|
||||
|
||||
<ul class="w-full mb-4 empty:mb-0">
|
||||
<li class="relative font-bold">{origin}</li>
|
||||
|
||||
{#each steps as step, index}
|
||||
<li class="relative flex items-center">
|
||||
<MbtaLine name={step.line} />
|
||||
<span>
|
||||
to
|
||||
<span class="font-bold relative">
|
||||
{#if errors[index]}
|
||||
<div class="absolute bottom-full left-full text-nowrap border border-red-500 text-red-500 bg-white/90 p-3 rounded-lg rounded-bl-none z-40 animate-error origin-bottom-left">
|
||||
{errors[index]}
|
||||
</div>
|
||||
{/if}
|
||||
{agency.stations[step.station].stop_name}
|
||||
</span>
|
||||
</span>
|
||||
{#if showRemovalButton}
|
||||
<button class="ml-auto text-gray-500" on:click={() => dispatch("remove", index)}>x</button>
|
||||
{/if}
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
|
||||
<style>
|
||||
li {
|
||||
@apply pl-5;
|
||||
}
|
||||
|
||||
li:before {
|
||||
content: "";
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
@apply bg-white rounded-full ring ring-gray-800;
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: calc(50% - 5px);
|
||||
left: 0;
|
||||
z-index: 5;
|
||||
}
|
||||
li:after {
|
||||
content: "";
|
||||
height: 100%;
|
||||
width: 4px;
|
||||
@apply bg-gray-800;
|
||||
position: absolute;
|
||||
left: 3px;
|
||||
}
|
||||
|
||||
li:first-of-type::after {
|
||||
@apply rounded-t-full;
|
||||
}
|
||||
li:last-of-type::after {
|
||||
@apply rounded-b-full;
|
||||
}
|
||||
</style>
|
||||
@@ -1,12 +1,14 @@
|
||||
<script>
|
||||
import { onMount } from "svelte";
|
||||
import MbtaLine from "./MbtaLine.svelte";
|
||||
import Route from "./Route.svelte";
|
||||
|
||||
export let route = []
|
||||
export let errors = []
|
||||
|
||||
export let lines
|
||||
export let stations
|
||||
export let origin
|
||||
export let showRemovalButton = true
|
||||
|
||||
let selectedLine = ""
|
||||
let selectedStation = ""
|
||||
@@ -34,20 +36,9 @@
|
||||
</script>
|
||||
|
||||
<div class="p-5 rounded-lg mb-5">
|
||||
<ul class="w-full mb-4 empty:mb-0">
|
||||
<li class="relative font-bold">{origin}</li>
|
||||
|
||||
{#each route as step, index }
|
||||
<li class="relative flex items-center">
|
||||
<MbtaLine name={step.line} />
|
||||
<span>
|
||||
to
|
||||
<span class="font-bold">{stations[step.station].stop_name}</span>
|
||||
</span>
|
||||
<button class="ml-auto text-gray-500" on:click={() => {route = route.filter((v,idx) => idx != index)}}>x</button>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
<Route steps={route} {errors} {origin} {showRemovalButton} on:remove={({detail}) => {
|
||||
route = route.filter((v, index) => index != detail)
|
||||
}} />
|
||||
|
||||
<div class="flex gap-2 items-center">
|
||||
<form on:submit|preventDefault={() => addStep()}>
|
||||
@@ -75,37 +66,6 @@
|
||||
</div>
|
||||
|
||||
<style>
|
||||
li {
|
||||
@apply pl-5;
|
||||
}
|
||||
|
||||
li:before {
|
||||
content: "";
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
@apply bg-white rounded-full ring ring-gray-800;
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: calc(50% - 5px);
|
||||
left: 0;
|
||||
z-index: 5;
|
||||
}
|
||||
li:after {
|
||||
content: "";
|
||||
height: 100%;
|
||||
width: 4px;
|
||||
@apply bg-gray-800;
|
||||
position: absolute;
|
||||
left: 3px;
|
||||
}
|
||||
|
||||
li:first-of-type::after {
|
||||
@apply rounded-t-full;
|
||||
}
|
||||
li:last-of-type::after {
|
||||
@apply rounded-b-full;
|
||||
}
|
||||
|
||||
select {
|
||||
@apply border border-blue-500 rounded-md;
|
||||
}
|
||||
|
||||
+14
-2
@@ -25,8 +25,12 @@ import screenshot from "../screenshot.png";
|
||||
<p
|
||||
class="fixed bottom-0 left-0 text-gray-400 transition-transform translate-y-3 hover:translate-y-0 hover:translate-x-2 cursor-default"
|
||||
>
|
||||
<a href="https://calebden.io" class="underline" target="_blank">caleb</a> made
|
||||
this
|
||||
<a href="https://calebden.io" class="underline" target="_blank">Caleb</a> made
|
||||
this · and it's on <a
|
||||
href="https://github.com/cjdenio/train-quiz"
|
||||
class="underline"
|
||||
target="_blank">GitHub</a
|
||||
>
|
||||
</p>
|
||||
|
||||
<p class="fixed bottom-1 right-2 text-gray-400">
|
||||
@@ -36,5 +40,13 @@ import screenshot from "../screenshot.png";
|
||||
target="_blank">MBTA</a
|
||||
>
|
||||
</p>
|
||||
|
||||
<script>
|
||||
import Honeybadger from "@honeybadger-io/js";
|
||||
Honeybadger.configure({
|
||||
apiKey: "hbp_kMocExYws1yKlIKXs0TkQzbAGmDpqJ2kz24F",
|
||||
environment: import.meta.env.MODE,
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in new issue
Block a user